14-1 基本概念
基本介绍
重载运算符是特殊名字的函数
名字:operator
后面加上需要重载的运算符号
构成:
- 参数列表
- 返回类型
- 函数体
注意点:
-
不能含有默认实参
-
第一个(左侧)运算对象绑定到隐式的this,所以显式的运算对象比实际的运算对象少一个
-
至少有一个类类型对象参数
//错误:不能重新定义内置的运算符 int operator+(int, int);
一些注意点
直接调用一个重载的成员函数
通常情况下,我们将运算符作用于类型正确的实参,从而以这种间接方式“调用”重载的运算符函数。然而,我们也能像调用普通函数一样直接调用运算符函数,先指定函数名字,然后传入数量正确、类型适当的实参:
//一个非成员函数的等价调用
data1 + data2; //普通的表达式
operator+(data1, data2);//等价的函数调用
//一个成员函数的等价调用
data1 += data2; //基于“调用”的表达式
data1.operator=(data2); //等价的函数调用
某些运算符不应该被重载
通常情况下,不应该重载逗号,取址,逻辑与和逻辑或运算符
详见c++primer491
使用与内置类型一致的含义
当你开始设计一个类时,首先应该考虑的是这个类将提供哪些操作。在确定类需要哪些操作之后,才能思考到底应该把每个类操作设成普通函数还是重载的运算符。如果某些操作在逻辑上与运算符相关,则它们适合于定义成重载的运算符:
- 如果类执行IO操作,则定义移位运算符使其与内置类型的IO保持一致。
- 如果类的某个操作是检查相等性,则定义 operator== ;如果类有了operator==,意味着它通常也应该有operator!=。
- 如果类包含一个内在的单序比较操作,则定义 operator<;如果类有了operator<,则它也应该含有其他关系操作。
- 重载运算符的返回类型通常情况下应该与其内置版本的返回类型兼容:逻辑运算符和关系运算符应该返回bool,算术运算符应该返回一个类类型的值,赋值运算符和复合赋值运算符则应该返回左侧运算对象的一个引用。
选择作为成员还是非成员
- 赋值(=)、下标([ ])、调用(())和成员访问箭头(->)运算符必须是成员。
- 复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
- 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员。
- 具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数。
如果具有对称性的运算符没有定义成非成员,那么将不支持类型转换,左侧对象必须是成员函数所在类的类型,失去了对称性
string s = "world";
string t = s + '!'; //正确:能把char*加到string上
string u = "hi" + s; //如果+是string的成员,会产生错误
如果operator+是string的成员,最后一行代码等价于
"hi".operator+(s);
显然是错误的。