不能被重载的运算符:
:: | .* | . | ? : |
重载&、|、和逗号运算符,无法保留运算对象求值顺序规则,短路求值属性。重载&&和||,无法保留内置运算符的。
重载取地址运算符和逗号运算符,会使得它们的行为,异于常态。
赋值运算符和复合赋值运算符,应该返回左侧运算对象的一个引用。
必须是成员 | =、[]、()、-> |
建议是成员 | ++、--、解引用 |
建议是非成员 | 算术、相等性、关系、为运算 |
例如:operator+是string类的非成员,则const char*没有成员函数的,也可以使用+。
重载输入运算符>>:
输入运算符必须处理输入可能失败的情况,而输出运算符不用。
重载时,函数的第一个参数,必须是将要读取的,流的引用,返回值也是该流的引用。
算术和关系运算符:
一般不会改变运算对象的状态,因此,重载函数的两个参数,都是常量的引用,返回值为非常量。
相等运算符:
如果类定义了operator==,也应该定义operator !=。某一个运算符的工作,可以委托给另一个。
关系运算符:
如果存在逻辑可靠的<定义,或类包含==,且<的定义和==产生的结果一致时,才定义<运算符。
赋值运算符:
接受花括号内的元素列表作为参数:
下标运算符:
该运算符,必须是成员函数。
如果一个类定义了下标运算符,会有两个版本:一个返回普通引用;一个是常量成员,并返回常量引用。
递增和递减运算符:
既要定义前置版本,也要定义后置版本。
在前置版本中,先调用check函数检验对象的有效性。
后置运算符:多提供一个int参数,作区分。
栗子:
成员访问运算符:
解引用和箭头运算符,相互配合:
ps:重载的箭头运算符,必须返回类的指针或,自定义箭头运算符的某个类对象。
上述类对象,调用箭头运算符的话,会返回某个指针,然后通过解引用的方式,找到某个操作。
函数调用运算符:
若类定义了调用运算符,则该类的对象,称作函数对象。
函数对象,常作为泛型算法的实参。如,打印容器的内容:
lambda是函数对象:
ps:用ShorterString替代lambda表达式后,重写stable_sort。ShorterString类里含有函数调用运算符。
用函数调用运算符的方式,替换lambda表达式中的捕获列表:
lambda表达式产生的类,是否含有默认的拷贝/移动构造函数,视捕获的数据成员类型而定。
标准库定义的函数对象:
直接使用关系运算符,比较指针,会产生未定义的行为。但可以使用标准库的函数对象less:
ps:即按地址从小到大排序。
可调用对象与function:
C++中可调用的对象:
函数 | 函数指针 | lambda表达式 | bind创建的对象 | 重载了函数调用运算符的类 |
若设计一个计算器,可定义一个map,将操作符和运算函数关联起来:
调用形式,可能都是int (int , int)的lambda表达式mod,却不能插入到map,map只收函数的指针:
可使用function模板解决上述问题:
重新定义map:
function类型重载了调用运算符,该运算符接受自己的实参,然后传递给存好的可调用对象:
重载、类型转换与运算符:
类型转换运算符:负责将一个类类型的值,转换成其它类型。
该运算符,不能转换数组或函数类型。但可转换成,指针或引用类型。
类型转换函数,必须是类的成员,没有形参列表和返回值,通常是const(不能改变类的状态)
若类类型和转换类型之间,不存在一对一的映射关系。最好别定义该运算符。
意外结果:
ps:cin被提升为bool,然后转换为int,被左移41个位置。
显式的类型转换运算符:
这就要显式地进行类型转换:
只有表达式,用作条件,才会发生隐式转换。
转换为bool:
在条件中,使用流对象,都会用到IO类型定义的,operator bool。
避免有二义性的类型转换:
对某个类来说,最好只定义最多一个与算术类型有关的转换规则。
最好不要在两个类之间,构建相同的类型转换。
如A类有:
B类有:
最后产生了如下二义性:
若转换函数前后,存在标准类型转换,则它将决定,最佳匹配到底是哪个。
最佳实践,假设定义了一个算术类型转换运算符,则通过标准类型转换,为其它算术运算符。
若调用重载函数时,需用构造函数或强制类型转换来改变实参类型,则程序设计不足。
重载函数与用户定义的类型转换:
函数匹配与重载运算符:
ps:一个类内,既提供了算术类型的类型转换,也提供了重载的运算符。
则会遇到如下,重载运算符与内置运算的二义性问题:
ps:0转化为SmallInt类型,还是s3转化为int类型?