首页
技术博客
PHP教程
数据库技术
前端开发
HTML5
Nginx
php论坛
新用户注册
|
会员登录
PHP教程
技术博客
编程问答
PNG素材
编程语言
前端技术
Android
PHP教程
HTML5教程
数据库
Linux技术
Nginx技术
PHP安全
WebSerer
职场攻略
JavaScript
开放平台
业界资讯
大话程序猿
登录
极速注册
取消
热门标签 | HotTags
hashset
uri
eval
hashtable
integer
timezone
jar
int
lua
import
split
plugins
filter
vba
object
utf-8
dockerfile
httprequest
ascii
main
python
include
web
tree
post
replace
future
bytecode
input
js
match
range
const
controller
cPlusPlus
email
keyword
blob
buffer
scala
fetch
golang
schema
format
uml
nodejs
list
php5
vbscript
flutter
default
install
merge
expression
loops
chat
regex
heap
httpclient
command
cookie
jsp
express
hashcode
triggers
version
settings
foreach
header
shell
subset
erlang
netty
window
bash
metadata
javascript
数组
timestamp
当前位置:
开发笔记
>
编程语言
> 正文
操作符重载浅析
作者:涛之圣首到 | 来源:互联网 | 2024-10-08 17:16
什么是操作符重载?一看到重载,很容易就让人联想到成员函数重载,函数重载可以使名称相同的函数具有不同的实际功能,只要赋给这些同名函数不同的参数就可以了,操作符重载也是基于这一机制的。系统为我们提供了许多
什么是操作符重载?
一看到重载,很容易就让人联想到成员函数重载,函数重载可以使名称相同的函数具有不同的实际功能,只要赋给这些同名函数不同的参数就可以了,操作符重载也是基于这一机制的。系统为我们提供了许多操作符,比如“+”,“[ ]”等,这些操作符都有一些默认的功能,而操作符重载机制允许我们给这些操作符赋予
不同的功能,
并能够按照普通操作符的使用格式来使用自己定义功能的操作符(即重载的操作符)
。
定义之后,我们就可以
按照平常使用操作符的格式来使用我们自己的重载操作符了。
操作符重载一般在
类内部定义,就像成员函数一样定义,这叫做类成员重载操作符。当然也可以在
类外定义
,即非类成员操作符重载。
二、为什么要使用操作符重载?
举例说明,比如类String,该类有这样一个功能,可以将两个字符串连接成一个字符串,为此,我们可以给类String定义一个成员函数实现此功能,可以给该函数取一个形象的名字,比如concatenate或append,但是相比较,这两个名字都不如操作符“+=”形象直观。在这种情况下,我们就可以定义操作符“+=”的重载,来实现此功能。
也就是说,如果要定义一个函数,而这个函数的功能与操作符的功能比较类似时,这个时候我们就可以定义重载操作符,而不使用通常的成员函数定义。这里所说的
操作符重载,指的是与系统定义的操作符重载,而不是说定义两个“
+=
”,这两个重载,这一点需要清楚。
但是这四个操作符不能用于重载:
:: * ? :
三、如何声明操作符重载?
同普通函数类似,只不过它的名字包括
关键字
operator
,以及紧随其后的一个预定义操作符
。例如:
String&
operator+=(const String&
)
;
String&
operator+=(const char*
)
;
注意:上面的括号表示形式参数,即使操作符重载不需要参数,也应该写上一个空的“( )”,而不是将其省略,这一点其实和普通函数的声明是类似的。其实,
声明的唯一区别就是
名字不同而已
。
四、怎样使用操作符重载?
两种操作符重载:类成员操作符重载和非类成员操作符重载。
1、类成员操作符重载
已知类String中声明了两个“==”操作符重载,分别是:
bool operator==(const char*) const;
bool operator==(const String&) const;
其中第一个重载的操作符允许我们比较一个String类对象是否等于一个C风格字符串,第二个允许我们比较两个String类对象是否相等。
示例代码:
#include
int main()
{
String flower;
If(flower==”lily”) //
正确:调用bool operator==(const char*) const;
……
else
if(“tulip”==flower) //
错误
…….
}
关键看一下,为什么第二个重载操作符的使用是错误的?
因为:
只有在左操作数是该类类型的对象时,才会考虑使用作为类成员的重载操作符。
因为这里的”tulip”不是String类型对象,所以编译器试图找到一个内置操作符,它可以有一个C风格字符串的左操作数,然而事实上并不存在这样的操作符,所以编译时产生错误。
疑问:我们可以使用String类的构造函数将一个C风格字符串,转换成一个String对象,为什么编译器不能做以上转换呢?即
if(String(“tulip”)==flower);//
这样就是正确的
答:为了效率和正确性
重载操作符并不要求两个操作数的类型一定相同。可能有这样一个类Text,这个类的构造函数的参数及其成员重载操作符的参数都与String类一致,如果使编译器能够自动将C风格字符串转换成某个类型的对象,那么编译器首先会检索所有的类定义,选择能够提供正确构造函数和重载操作符的类进行转换,这无疑会增加程序的编译时间,还有就是类String和类Text均合适,编译器也不知道该将C风格字符串转换成String还是Text对象了。
对于类成员重载操作符
,隐式的this指针被用作隐式的第一个参数,对于成员操作符,
flower==”lily”
会被编译器重写为:
flower.operator==(“lily”);
2、非类成员操作符重载
为了解决上面的问题,我们可以考虑使用
非类成员操作符代替类成员操作符,这样做的好处是
左操作数不必非要是某个类的类型对象
了,
对于需要两个操作数的操作符重载,我们就可以定义两个参数了
。比如:
bool operator==(const String&,const String&);
bool operator==(const String&,const char*);
可以看到,这两个全局重载操作符比成员操作符多了一个参数。
这样定义之后,还是上面的代码,当调用flower==”lily”时,会调用上面的bool operator==(const String&,const char*);。
然而“tulip”==flower会调用哪个操作符重载呢,我们并没有定义bool operator==(const char*,const String&);,我们是不是必须定义这样一个全局操作符重载呢?答案是否定的,因为当一个重载操作符是一个名字空间函数时,对于操作符的第一个和第二个参数,即等于操作符的左右两个操作数都会考虑转换,就像
int vi=1; double vd=2.0; vi=vi+vd; 会先将vi转换成double型,再做加法一样
这意味着,编译器将解释第二个用法如下:
bool operator==(String(“tulip”),flower)。这样会增加系统转换开销。
因此,如果需要频繁比较C风格字符串和String对象,那么最好定义上面的操作符重载,如果不频繁,我们只需定义下面一个就够了:
bool operator==(const String&,const String&);
分析:什么时候定义类成员操作符重载,什么时候定义非类成员操作符重载?
答:(1)如果一个重载操作符是类成员,那么只有当跟它一起使用的左操作数是该类对象时,它才会被调用,如果该操作符的左操作数必须是其他类型,那么重载操作符必须是非类成员操作符重载。
(2)C++要求,赋值(=),下标([ ]),调用(())和成员访问箭头(->)操作符必须被指定为类成员操作符,否则错误。
我们只能为
类类型或枚举类型的操作数定义重载操作符,我们可以这样实现:把重载操作符声明为类的成员,或者
声明为非类成员重载操作符但同时至少有一个类或者枚举类型的参数(按值传递或按引用传递)。
这句话的意思,实际是说我们不能修改内置类型的操作符重载或添加操作符重载,比如下面的声明便是错误的:
int operator+(int,int);
这改变了内置类型int的+操作符的功能,所以错误。
操作符重载相关知识点:
五、友元(friend)
考虑到类成员操作符重载可以访问类中的私有变量,但是非类成员重载操作符却不能很方便的访问类的私有成员,为了方便起见,我们可以通过使用
友元(
friend
)
的方式,方便的访问类的私有成员。
举例:
class String
{
friend
bool operator==(const String&,const String&);
friend
bool operator==(const String&,const char*);
public:
//……..
private:
//………
}
注意:
friend
声明紧跟在类名之后,而不是放在
public
、
private
或
protected
中,因为友元不是授权类的成员,并且该关键字只能出现在类中。
经过上述声明之后,我们的非类成员重载操作符就可以直接方位String类的私有成员了。当然我们也可以不使用友元,而使通过该类的共有成员函数来间接访问该类的私有成员也是可以的,内联函数inline就不错,效率也不低。
由此看来,声明友元(
friend
)主要是为了方便高效的访问类的私有成员变量。
分析:什么时候应该使用友元:
(1)
某个类不提供公有的访问私有成员的函数。
(2)
使用共有成员函数访问私有成员变量效率比较差时。
友元除了用在非成员的重载操作符外,一个名字空间函数(比如全局函数),另一个在此之前定义的类的成员函数或者一个完整的类,均可以声明为某个类的成员。
如下声明:
class B;
class A
{
friend class B;
public:
//…….
Private:
//……
}
通过上述声明,类A的成员函数可以访问所有的类B的私有成员,同样类B的成员函数可以访问所有类A的私有成员。
六、
类类型对象的隐式类型转换
我们知道,系统提供的内置类型有隐式类型转换的功能,实际上
我们也可以为自己编写的类提供隐式类型转换功能。这样,当我们在使用这种类的对象时,如果需要,编译器会自动调用该类的类型转换函数,实现类型转换功能。
C++提供了这样一种机制,通过它,每个类都可以定义一组“可被应用在该类型对象上的转换”
举例:
class SmallInt
{
public:
SmallInt(int ival):val(ival){};
operator int(){return value;} ;//
类型转换操作符
SmallInt->int
private:
int value;
}
在上例中,操作符int()是一个转换函数,它定义了一个用户转换,实现在类类型和转换函数中指定的类型之间的转换,本例中,目标类型是int。
经过上面的定义后,SmallInt对象便可以用在任何可以使用int的地方,例如:
SmallInt si(3);
si+3.1415926;
这样,首先调用SmallInt转换函数,产生int型值3,然后将3变为3.0,和3.1415926相加。
实际上,上面类中定义转换函数还有一个好处就是:
省略了为该类定义重载操作符,尤其是重载操作符参数类型多样时。
1、
转换函数
格式:
operator type( ); //
参数必须为空,且
( )
不能省略,并且无返回值类型。
说明:
type
表示目标类型,它可以用内置类型,类类型或
typedef
名取代,但不允许表示数组,转换函数必须是类的成员函数,并且不能指定返回类型和参数表。
显式的强制类型转换会导致调用转换函数
,如果被转换值的类型是一个类类型,它有一个转换函数,并且该转换函数的类型是强制转换所指定的类型,则调用这个类的强制转换函数。
比如:
char* tokName=
static_cast
(tok);//tok
表示对象,它所属的类提供一个到char*的转换函数。
2、
用构造函数作为转换函数
在一个类的构造函数中,凡是
只带有一个参数的构造函数,例如
SmallInt的构造函数SmallInt(int),都定义了一组隐式转换,
把构造函数的参数类型转换成该类类型。
注意:是将int转换成SmallInt型。和前面说的转换函数功能正好相反。
举例:
void calc(SmallInt);
int vi;
calc(vi);
这时,编译器就会隐式调用类SmallInt的构造函数SmllInt(int),将vi转换成SmallInt对象,然后再将这个对象传递给函数calc( )。
可以这样理解:
{
SmallInt temp=SmallInt(i);
Calc(temp);
}
这个大括号指出temp这个临时对象的生命周期。
注意:编译器不会使用一个显式构造函数(关键字explicit标志)来执行隐式类型转换,但是却可以使用这样的构造函数来进行强制转换(即static_cast<>)。
string
const
char
include
int
main
ip
编译
text
写下你的评论吧 !
吐个槽吧,看都看了
会员登录
|
用户注册
推荐阅读
include
简单数据结构模板
一,STL1&gt;STL中数据结构常见操作a.assign(b.begin(),b.begin()+3);b为向量,将b的0~2个元素构成的向量赋给aa.as ...
[详细]
蜡笔小新 2024-10-08 11:27:29
include
<C/C++>输入整型数组和排序标识,对其元素按照升序或降序进行排序
题目描述输入整型数组和排序标识,对其元素按照升序或降序进行排序(一组测试用例可能会有多组数据)本题有多组输入,请使用whil ...
[详细]
蜡笔小新 2024-09-30 18:28:29
include
UNP总结 Chapter 12~14 IPv4与IPv6的互操作性、守护进程和inet超级服务器、高级I/O函数
一、IPv4与IPv6的互操作性1.IPv4客户与IPv6服务器拥有双重协议栈的主机的一个基本特性就是:其上运行的IPv6服务器既能应付IPv4客户,又能应付IPv6客户。这是通过使用IPv4映射 ...
[详细]
蜡笔小新 2024-09-30 18:55:51
filter
贴图的支持及设置:关于贴图分辨率的支持及设置的用户指南
http:hi.baidu.comdbfr2011818itemeef1eac8df31a2d69744520b贴图分辨率虚幻引擎3支持的贴图分辨率是从1x1到4096x4096 ...
[详细]
蜡笔小新 2024-09-30 13:00:03
tree
解题:洛谷2093 JZPFAR
题面初见K-DTree其实这样的题(欧几里得距离第$x$近点对)不应该用K-DTree做,因为会被构造数据卡成$O(n^2)$,随机的另说。但是并没有找 ...
[详细]
蜡笔小新 2024-10-08 19:25:45
int
Linux命令与文件——硬件信息获取命令与相关文件
概述:Linux的哲学思想“有众多功能目的单一的小程序,并且组合这些小程序来完成复杂任务”,而这些小程序有相当一部分都是我们所要用的命令& ...
[详细]
蜡笔小新 2024-10-08 17:05:12
tree
树链剖分 学习整理
“在一棵树上进行路径的修改、求极值、求和”乍一看只要线段树就能轻松解决,实际上,仅凭线段树是不能搞定它的。我们需要用到一种貌似高级的复杂算法——树链剖分。 树链,就是树上的路径。剖分, ...
[详细]
蜡笔小新 2024-10-08 15:54:39
plugins
org.eclipse.emf.ecore.plugin.EcorePlugin.computePlatformURIMap()方法的使用及代码示例
本文整理了Java中org.eclipse.emf.ecore.plugin.EcorePlugin.computePlatformURIMap()方法的一些代码示例, ...
[详细]
蜡笔小新 2024-10-08 15:47:28
input
2018.5.23(二叉树中序遍历中查找某一结点的后继结点)
#include#include#includeusingnamespacestd;structTree{intdata;Tr ...
[详细]
蜡笔小新 2024-10-08 13:49:23
js
CTF编程入门第一题 循环
循环分值:10循环分值:10来源:北邮天枢战队难度:易参与人数:1478人GetFlag:467人答题人数:523人解题通过率:89%来源:北邮天枢战队难度:易参与人数:1478人 ...
[详细]
蜡笔小新 2024-10-08 13:41:34
main
Java:多线程,java.util.concurrent.atomic包之AtomicInteger/AtomicLong用法
1.背景java.util.concurrent.atomic这个包是非常实用,解决了我们以前自己写一个同步方法来实现类似于自增长字段的问题。在Java语言中,增量操作符(++)不是原子的, ...
[详细]
蜡笔小新 2024-09-30 21:25:22
js
POJ2253(floyd)
FroggerTimeLimit:1000MSMemoryLimit:65536KTotalSubmissions:32257Accepted:10396DescriptionFr ...
[详细]
蜡笔小新 2024-09-30 20:13:09
js
九宫格计算
九宫格计算. ...
[详细]
蜡笔小新 2024-09-30 19:37:45
js
GoogleLog(GLog)源码分析
本文分析和介绍了GLo ...
[详细]
蜡笔小新 2024-09-30 15:14:04
main
统计一个文件calcCharNum.txt(见附件)中字母‘A‘和‘a‘出现的总次数
importjava.io.BufferedReader;importjava.io.File;importjava.io.FileReader;importjava.io. ...
[详细]
蜡笔小新 2024-09-30 13:46:51
涛之圣首到
这个家伙很懒,什么也没留下!
Tags | 热门标签
hashset
uri
eval
hashtable
integer
timezone
jar
int
lua
import
split
plugins
filter
vba
object
utf-8
dockerfile
httprequest
ascii
main
python
include
web
tree
post
replace
future
bytecode
input
js
RankList | 热门文章
1
云原生的十大开源项目是什么
2
微信上线「行程查询」服务;钉钉 CEO 回应被打「一星」;Go 1.14 发布 | 极客头条...
3
QPainter QPen QBrush
4
Flume 入门及疑惑
5
FLEX学习建议——dreamer
6
手机端的1px问题
7
多级评论 php,免费的评论接口(支持多级嵌套)
8
肩膀关节咔咔弹响,跟掰手指一样的声音
9
c语言单元测试五答案,C语言程序设计(西安工程大学)2020知到单元测试答案
10
【刷题】【博弈论】三国游戏
11
乐趣_三个水桶等分8升水的问题《算法的乐趣》
12
清华来了第二位菲尔兹奖得主,是丘成桐力荐的老朋友Caucher Birkar
13
[转] word2vec
14
广义二项式定理求解系数
15
TTL、CMOS和RS232电平的区别和联系
PHP1.CN | 中国最专业的PHP中文社区 |
DevBox开发工具箱
|
json解析格式化
|
PHP资讯
|
PHP教程
|
数据库技术
|
服务器技术
|
前端开发技术
|
PHP框架
|
开发工具
|
在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved |
京公网安备 11010802041100号
|
京ICP备19059560号-4
| PHP1.CN 第一PHP社区 版权所有