c++ primer 第十四章重载运算与类型转换

14.1 基本概念

重载运算符参数数量与运算符的运算对象数量一样多。除了operator()之外其它重载运算符不能含有默认实参。

如果一个运算符是成员函数,第一个运算对象隐式绑定为this指针。
运算符函数或者是成员函数,或者参数至少有一个类类型。
可以重载大多数运算符但不能发明新的。优先级和结合律不变。

某些运算符不应该被重载,如逻辑与逻辑或和逗号运算符、取地址运算符等等。

重载的运算符应该与原运算符操作类似。如果有算数运算符或者位运算符,一般也提供复合赋值运算符。

作为成员函数时左侧运算对象必须是运算符所属类的一个对象。

14.2 输入和输出运算符

14.2.1 重载输出运算符<<

第一个形参是ostream对象的引用,第二个一般是常量的引用。返回ostream形参。

尽量减少格式化操作如换行。重载输入输出必须是非成员函数,因为输入输出的类是标准库类型。

14.2.2 重载输入运算符>>

第一个形参是istream对象的引用,第二个是个非常量的引用,返回istream形参。

输入运算符必须考虑输入可能失败的情况。

14.3 算术和关系运算符

如果定义了算术运算符,一般也会定义相应的复合赋值运算符。此时最有效的方式是使用复合赋值来定义算术运算符。

14.3.1 相等运算符

相等运算与不相等运算互相依赖,一个可以委托给另一个。

14.3.2 关系运算符

  1. 定义顺序关系,令其与关联容器中对关键字的要求一致。
  2. 如果类同时含有== 运算符,那么定义一种关系与 ==保持一致。特别是两个对象是!=的,那么一个对象应该<另外一个。

14.4 赋值运算符

不同于拷贝赋值和移动赋值运算符,普通赋值运算符可以接收别的类型作为右侧运算对象。返回左侧运算对象的引用。

通常把包括复合赋值在内的赋值运算都定义在类的内部。

14.5 下标运算符

下标运算符必须是成员函数。返回所访问元素的引用,一般需要定义常量版本和非常量版本。

14.6 递增和递减运算符

递增递减运算符定义为类的成员。前置运算符返回递增或递减后对象的引用。

使用operator++(int)表示后置递增,即添加一个不会使用的int参数。

使用前置版本来完成后置版本的操作。如果要显示调用后置版本,传递一个整数参数。

14.7 成员访问运算符

箭头运算符(->)必须是类的成员,解引用运算符(*)一般也是。定义为const成员。
箭头运算符必须是获取成员的操作。
根据point类型的不同,point->mem分别等价于:

  • (*point).mem; //point 是一个内置的指针类型
  • point.operator()->mem; //point是类 的一个对象

point->mem执行过程如下:

  1. 如果point是指针,那么表达式等价于(*point).mem。
  2. 如果point是定义了operator->的类的一个对象,那么使用point.operator->()的结果来获取mem。如果该结果是个指针执行第一步,如果结果也含有->成员函数,重复此步骤知道返回结果或出错。

14.8 函数调用运算符

如果类定义了调用运算符,则该类的对象成为函数对象。
函数对象常常作为泛型算法的实参,而且与函数不同可以含有内部状态。

14.8.1 lambda是函数对象

当我们编写一个lambda后,编译器将该表达式翻译为一个未命名类的未命名对象。

默认情况下lambda不能改变它捕获的变量,因此由它产生的类的函数调用运算符是一个const成员函数。

lambda表达式产生的类不含默认构造函数、赋值运算符及默认析构函数;是否含有默认的拷贝/移动构造函数通常视捕获的数据成员类型而定。

14.8.2 标准库定义的函数对象

标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类。

表示运算符的函数对象类常用来替换算法中的默认运算符。

14.8.3 可调用对象与function

C++中有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。

不同类型的可调用对象可能共享同一种调用形式。比如int(int, int)。

标准库function,定义在functional头文件中,可以将同一种调用形式的不同可调用对象作为一个类型的对象。如function<int(int,int)>,可以使用map<string, function<int(int,int)>>将不同类型的可调用对象放到一个map中。

不能直接将重载函数的名字放入function类型的对象中,可以使用函数指针或者lambda来解决二义性。

14.9 重载、类型转换与运算符

转换构造函数和类型转换运算符共同定义了类类型转换,有时也成为用户定义的类型转换。

14.9.1 类型转换运算符

形式如:operator type() const; type为转换类型。转换到的类型需要能够作为函数的返回类型。不允许转换为数组或者函数类型,但允许转换为指针或者引用类型。

类型转换运算符可能会产生意料之外的计算结果。如istream可以转换为bool型的话那么 cin<<i 可以转换为数字0或1的左移位。

现实的类型转换运算符可以防止这样的异常。不过当表达式用作条件时,会将显示的类型转换也隐式转换为bool型。

因为在条件判断是会自动转换为bool型,所以bool型转换一般定义为显示转换。

14.9.2 避免有二义性的类型转换

如果类中包含一个或多个类型转换,则必须确保在类类型和目标类型之间只存在唯一一种转换方式。有两种情况会导致多重转换路径:

  1. 两个类提供相同的类型转换。如A类接收B作为参数有个转换构造函数,而B类有转换为A的类型转换运算符。
  2. 类定义了多个转换规则。比如算术运算符相关的转换规则。

如果类定义了一组类型转换,它们的转换源或者转换目标类型本身可以通过其它类型转换联系到一起,也会产生二义性问题。如类中定义了多个参数都是算术类型的构造函数,或者转换目标都是算术类型的类型转换符。

如果两个或多个类型转换都提供了同一种可行匹配,则这些类型转换一样好。

如果两个或多个用户定义的类型转换都提供了可行匹配,那么认为这些类型转换一样好。转换级别顺序只有调用同一个用户定义的类型转换时才有用。

14.9.3 函数匹配与重载运算符

重载的运算符也是重载的函数。

和普通函数调用不同,我们无法通过调用形式区分当前调用的是成员函数还是非成员函数。因此在函数匹配时两者都需要考虑。

如果对同一个类既提供了转换目标是算术类型的类型转换,也提供了重载的运算符,那么会遇到重载运算符与内置运算符的二义性问题。

猜你喜欢

转载自blog.csdn.net/qq_25037903/article/details/82937841