8.13 章节小结
运算符<<在 C++ 中有多种用途,既可以用作流插入运算符又可以用作左移位运算符,这是运算符重载的一个范例。同样,运算符>>也是C++中的一个重载运算符,它既可以用作流读取运算符,也可以用作右移位运算符。
为了使运算符在不同的上下文中具有不同的含义,C++ 允许程序员重载大多数运算符。编译器根据运算符的使用方式产生合适的代码。
运算符重载提高了C++的可扩展性。
运算符重载是通过编写函数定义实现的。函数名是由关键字operator和其后要重载的运算符符号组成的。
用于类的对象的运算符必须重载,但是有两种例外情形。对于相同类的两个对象使用赋值运算符而不用重载,默认的方式是复制数据成员。地址运算符(&)也无需重载就可以用于任何类的对象,它返回对象在内存中的地址。
C++为其内部类型提供了的丰富的运算符集,重载这些运算符的目的是为用户自定义的类型提供同样简洁的表达式。
重载不能改变运算符的优先级和结合律。
重载不能改变运算符操作数的个数。重载的一元运算符仍然是一元运算符,重载的二元运算符仍然是二元运算符。C++惟一的三元运算符(?:)不能被重载。
不能建立新的运算符符号,只有现有的运算符才能被重载。
运算符重载不能改变该运算符用于内部类型的对象时的含义。
在重载运算符()、[]、->,或者=时,运算符重载函数必须声明为类的一个成员。
运算符函数既可以是成员函数,也可以是非成员函数。
当运算符函数是一个成员函数时,最左边的操作数必须是运算符类的一个类对象(或者对该类对象的引用)。
如果左边的操作数必须是一个不同的类的对象,该运算符函数必须作为一个非成员函数来实现。
只有当二元运算符的最左边的操作数是该类的一个对象或者当一元运算符的操作数是该类的一个对象时,才会调用运算符成员函数。
选择非成员函数重载运算符的另外一个原因是使运算符具有可交换性。例如,绐定正确的重载运算符定义,运算符左边的参数可以是其他数据成员的对象。
类的一元运算符可重载为一个没有参数的非static成员函数或者带有一个参数的非成员函数,参数必须是用户自定义类型的的对象或者对该对象的引用。
二元运算符可以重载为带有一个参数的非static成员函数或者带有两个参数的非成员函数(参数之一必须是类的对象或者是对类的对象的引用)。
数组下标运算符不仅仅可用于数组,还可以用于从其他各种顺序容器类(如链表、字符串、字典等等)中选择元素。此外,下标不仅仅可以是整数,还可以是字符或者字符串等等。
复制构造函数根据同类中的其他对象初始化一个对象。不论何时需要复制对象时都会调用复制构造函数,例如在按值调用时,或是从被调用函数返回值时。在复制构造函数中,被复制的对象是通过引用传递的。
编译器不知道怎样实现用户自定义类型和内部类型之间的转换,程序员必须明确地指明如何进行转换。这种转换可以用转换构造函数实现(即带有单个参数的构造函数),这种函数仅仅把其他类型的对象转换为某个特定类的对象。
转换运算符(又称为强制类型转换运算符)可以把一种类的对象转换为其他类的对象或内部类型的对象。这种运算符必须是一个非static成员函数,而不能是友元函数。
转换构造函数是带有一个参数的构造函数,用来把参数转换为构造函数所在类的对象c编译器可隐式调用这种构造函数。
赋值运算符是最常用的重载运算符,通常用来把一个对象赋给相同类的另外一个对象。通过使用转换构造函数,赋值运算符也能够使不同类的对象之间相互赋值。
在不提供重载的赋值运算符时,赋值运算符的默认行为是复制类的数据成员。在有些情况下这是允许的,但是当对象中包含指向动态分配的内存区的指针时,成员复制会导致两个不同的对象指向同一块动态分配的内存区。这样,调用其中一个对象的析构函数将释放该动态分配的内存块,如果另一个对象引用该内存区,其结果是不确定的。
要重载既能允许前置又能允许后置的自增运算符,每个重载的运算符函数必须有一个明确的特征,以使编译器能确定要使用的++版本。重载前置++的方法与重载其他前置一元运算符一样。向后置自增运算符函数提供第二个参数(必须是int类型)达到了把前置和后置自增运算符函数区分开来的目的。实际上,用户并没有给该特定的整数参数提供值,它仅仅是让编译器区分前置和后置自增运算符函数。