第十四章:重载运算与类型转换

第十四章:重载运算与类型转换

当运算符被用于类类型的运算对象时,C++允许我们为其指定新的含义;同时我们也能自定义类类型之间的转换规则。

一.基本概念

重载运算符是具有特殊函数名的函数,本质上来说重载运算符就是一个函数!,函数为operator加上要重载的运算符。

重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。如果一个运算符函数是成员函数,则第一个运算对象绑定到隐式的this指针上,所以成员运算符函数的显式参数数量比运算符的运算对象总数少一个。除了重载的函数调用运算符operator()之外,其他的重载运算符不能含有默认实参。

对于一个运算符函数来说,它或者说类的成员,或者至少含有一个类类型的参数。所以当运算符作用于内置类型的运算对象时,我们无法改变该运算符的含义。

重载的运算符优先级和结合律不变。

可以间接也可以直接调用重载的运算符函数。

通常情况下,不应该重载无法保留求值顺序和短路求值的运算符,即逗号,取地址,逻辑与或。

当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象。

二.输入和输出运算符

通常情况下,输出运算符的第一个参数是非常量的ostream引用(因为要改变输出流的状态),第二个参数是要输出对象的常量引用。一般返回ostream形参。

输出运算符应该尽量减少格式化操作如换行,将控制权交给用户。

与iostream标准库兼容的输入输出运算符必须是普通的非成员函数(友元),因为在iostream中就是非成员函数,保持一致。

输入运算符必须处理输入可能失败的情况,输出运算符不需要。

三.算术和关系运算符

通常把算术和关系运算符定义为非成员函数以允许对左侧或右侧的运算对象进行转换,形参都是常量的引用。

如果类同时定义了算术运算符合符合赋值运算符,应该使用复合赋值来实现算术运算符。

相等运算符和不相等运算符中的一个应该把工作委托给另一个。

如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含运算符,则当且仅当<的定义和产生的结果一致时才定义<运算符。

四.赋值运算符

赋值运算符也就是=运算符。

在拷贝赋值和移动赋值运算符之外,还可以重载花括号的那种赋值,参数为一个initializer_list<>。

赋值运算符必须定义为类的成员,复合赋值运算符通常也应该这样做,两者都应该返回左侧运算对象的引用。

五.递增和递减运算符

普通的重载形式无法区分前置和后置运算符,为了解决这个问题,后置版本接收一个额外的(不被使用)int类型的形参,当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。

六.成员访问运算符

重载的箭头运算符->必须返回类的指针或者自定义了箭头运算符的某个类的对象。

七.函数调用运算符

如果类定义了调用运算符,则该类的对象称作函数对象,因为该类的对象行为像函数一样。

lambda也是函数对象。因为当我们编写了一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。在lambda表达式产生的类中含有一个重载的函数调用运算符,之所以我们不能修改捕获的对象,因为该函数是const的。捕获的变量放在未命名类的私有成员变量中,用构造函数初始化它。

标准库定义了一组表示算术运算符,关系运算符和逻辑运算符的类,每个类分别定义了一执行命名操作的调用运算符,他们都是模板类,我们可以使用这些类产生函数对象。例如greater()就是产生了一个函数对象。

有些可调用对象属于同一种调用类型,例如int(int, int),那有时候我们想要把这些类型统一起来,例如放到同一个map中,但是函数和lambda和函数对象实际类型是不同的,map的第二个模板参数就不能确定,这个时候可以用标准库中标准类function来解决这个问题,function是一个模板类,用来存储某一类对象,例如function<int(int,int)>就可以用来存储上述的可调用对象,且现在类型统一为了function<int(int, int)>,可以存储在map中了。

八.重载、类型转换与运算符

类类型可以将其他类型转换为该类型,它也可以转换为其他类型,转换构造函数(前者)和类型转换函数(后者)共同定义了类类型转换。对于前者其实就是构造函数。

类型转换运算符是一种特殊成员函数,其形式为 operator type() const; type是要转换成的类型,不声明返回类型,形参列表为空,一般不能改变对象所以为const。类型转换运算符可以面向任意类型,只要该类型能作为函数的返回类型(除了void)。

如果未指明则类型转换运算符可以显式也可以隐式,但隐式的转换可能会存在一定问题,可能会在不该转换的时候转换。为了避免这样的问题,所以我们可以用explicit显式的指定。显式指定后必须要显式的类型转换才会转换,不过有一个例外,如果表达式被用作条件,则编译器会将显式的类型转换自动应用于它,这也是很多类型都定义了显式向bool类型转换的原因。

类型转换可能具有二义性,尤其是在以下两种情况:

  • A有一个构造函数可以将B构造为A,同时B有一个类型转换运算符可以转换为A,那么当B转换为A的时候,就有产生二义性;
  • 如果A有多个向内置算术类型转换的类型转换运算符,例如向int和double,那么如果要转向long double的时候,就会产生二义性。

所以要注意上面两个问题。

总之类型转换运算符比较容易产生二义性的问题。

练习

无答案的部分请查看答案

14.1

不同点:重载运算符至少有一个类类型对象作为运算对象;重载运算符不保证求值顺序和短路求值;
相同点:对于优先级和结合性及操作数的数目都不变。

14.3

(a)C++内置版本的==
(b)string的==
(c)vector的==
(d)string的==

14.11

有错误,没有处理输入错误的情况。

14.14

减少重复工作,提高可读性。

14.29

因为常量对象不能修改自己的成员,对于要修改自身状态的函数const版本无意义。

14.41

更方便的编写一个可调用对象(函数对象)。

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值