1. 重载运营商必须有一个类类型的操作数
对于内置类型运营商。它的意义不能改变。
例如,内置整数加法运算不能被重新定义:
// error: cannotredefine built-in operator for ints
int operator+(int, int);
也不能为内置数据类型重定义加号操作符。比如,不能定义接受两个数组类型操作数的operator+。
重载操作符必须具有至少一个类类型或枚举类型的操作数。这条规则强制重载操作符不能又一次定义用于内置类型对象的操作符的含义。
2. 优先级和结合性是固定的
操作符的优先级、结合性或操作数目不能改变。无论操作数的类型和操作符的功能定义怎样,表达式
x == y +z;
总是将实參y 和z 绑定到operator+。而且将结果用作operator== 右操作数。
有四个符号(+,-, * 和&)既可作一元操作符又可作二元操作符,这些操作符有的在当中一种情况下能够重载,有的两种都能够。定义的是哪个操作符由操作数数目控制。
除了函数调用操作符operator() 之外,重载操作符时使用默认实參是非法的。
3. 类成员与非成员
重载一元操作符假设作为成员函数就没有(显式)形參。假设作为非成员函数就有一个形參。
类似地,重载二元操作符定义为成员时有一个形參。定义为非成员函数时有两个形參。
类Sales_item 中给出了成员和非成员二元操作符的良好样例。
我们知道该类有一个加号操作符。
由于它有一个加号操作符,所以也应该定义一个复合赋值(+=)操作符。该操作符将一个Sales_item 对象的值加至还有一个Sales_item对象。
一般将算术和关系操作符定义非成员函数,而将赋值操作符定义为成员:
// member binaryoperator: left-hand operand bound to implicit this pointer
Sales_item&Sales_item::operator+=(const Sales_item&);
// nonmember binaryoperator: must declare a parameter for each operand
Sales_itemoperator+(const Sales_item&, const Sales_item&);
4. 不要重载具有内置含义的操作符
赋值操作符、取地址操作符和逗号操作符对类类型操作数有默认含义。
假设没有特定重载版本号,编译器就自定义下面这些操作符。
• 合成赋值操作符(第13.2 节)进行逐个成员赋值:使用成员自己的赋值:使用成员自己的赋值操作依次对每一个成员进行赋值。
• 默认情况下,取地址操作符(&)和逗号操作符(,)在类类型对象上的运行,与在内置类型对象上的运行一样。取地址操作符返回对象的内存地址,逗号操作符从左至右计算每一个表达式的值,并返回最右边操作数的值。
• 内置逻辑与(&&)和逻辑或(||)操作符使用短路求值。假设又一次定义该操作符,将失去操作符的短路求值特征。
通过为给定类类型的操作数重定义操作符,能够改变这些操作符的含义。
重载逗号、取地址、逻辑与、逻辑或等等操作符通常不是好做法。
这些操作符具有实用的内置含义,假设我们定义了自己的版本号,就不能再使用这些内置含义。
有时我们须要定义自己的赋值运算。
这样做时。它应表现得类似于合成操作符:赋值之后,左右操作数的值应是同样的。而且操作符应返回对左操作数的引用。重载的赋值运算应在赋值的内置含义基础上进行定制,而不是全然绕开。
5. 审慎使用操作符重载
每一个操作符用于内置类型都有关联的含义。比如。二元+ 与加法是全然同样的。将二元+ 相应到一个类类型的类似操作可提供方便的简写方法。
比如,标准库的类型string。遵循很多程序设计语言的通用规范,使用+ 表示连接——将一个串“加”至还有一个串。
当内置操作符和类型上的操作存在逻辑相应关系时,操作符重载最实用。使用重载操作符而不是创造命名操作,能够令程序更自然、更直观,而滥用操作符重载使得我们的类难以理解。
在实践中非常少发生明显的操作符重载滥用。比如,不负责任的程序猿可能会定义operator+ 来运行减法。更常见但仍不可取的是。改变操作符的“正常”含义以强行适应给定类型。操作符应该仅仅用于对用户而言无二义的操作。
在这里所谓有二义的操作符。就是指具有多个不同解释的操作符。
当一个重载操作符的含义不明显时,给操作取一个名字更好。对于非常少用的操作,使用命名函数通常也比用操作符更好。假设不是普通操作,没有必要为简洁而使用操作符。
6. 选择成员或非成员实现
为类设计重载操作符的时候。必须选择是将操作符设置为类成员还是普通非成员函数。在某些情况下,程序猿没有选择。操作符必须是成员。在还有一些情况下。有些经验原则可指导我们做出决定。以下是一些指导原则,有助于决定将操作符设置为类成员还是普通非成员函数:
• 赋值(=)、下标([])、调用(())和成员訪问箭头(->)等操作符必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。
• 像赋值一样,复合赋值操作符通常应定义为类的成员。与赋值不同的是,不一定非得这样做,假设定义非成员复合赋值操作符,不会出现编译错误。
• 改变对象状态或与给定类型紧密联系的其它一些操作符,如自增、自减和解引用,通常就定义为类成员。
• 对称的操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。
7. 赋值必须返回对*this 的引用
string 赋值操作符返回string 引用。这与内置类型的赋值一致。并且,由于赋值返回一个引用。就不须要创建和撤销结果的暂时副本。
返回值一般是左操作数的引用,比如。这是Sales_item 复合赋值操作符的定义:
// assumes that bothobjects refer to the same isbn
Sales_item&Sales_item::operator+=(const Sales_item& rhs)
{
units_sold +=rhs.units_sold;
revenue +=rhs.revenue;
return *this;
}
一般而言,赋值操作符与复合赋值操作符应返回操作符的引用。
8. 原型下标操作符
类定义下标操作符时,一般须要定义两个版本号:一个为非const 成员并返回引用,还有一个为const 成员并返回const 引用。
以下的类定义了下标操作符。为简单起见,假定Foo 所保存的数据存储在一个vector<int>: 中:
class Foo {
public:
int &operator[](const size_t);
const int&operator[] (const size_t) const;
// other interfacemembers
private:
vector<int>data;
// other member dataand private utility functions
};
下标操作符本身可能看起来像这样:
int&Foo::operator[] (const size_t index)
{
return data[index];// no range checking on index
}
const int&Foo::operator[] (const size_t index) const
{
return data[index];// no range checking on index
}
9. 转换可能引起内置操作符的二义性我们再次扩展SmallInt 类。这一次,除了到int 的转换操作符和接受int 參数的构造函数之外,将添加一个重载的加操作符:
class SmallInt {
public:
SmallInt(int = 0); //convert from int to SmallInt
// conversion to intfrom SmallInt
operator int() const{ return val; }
// arithmetic operators
friend SmallInt
operator+(constSmallInt&, const SmallInt&);
private:
std::size_t val;
};
如今,能够用这个类将两个SmallInts 对象相加,可是,假设试图进行混合模式运算,将会遇到二义性问题:
SmallInt s1, s2;
SmallInt s3 = s1 +s2; // ok: uses overloaded operator+
int i = s3 + 0; //error: ambiguous
第一个加使用接受两个SmallInt 值的+ 的重载版本号。第二个加有二义性,问题在于,能够将0 转换为SmallInt 并使用+ 的SmallInt 版本号,也能够将 s3 转换为int 值并使用int 值上的内置加操作符。
既为算术类型提供转换函数,又为同一类类型提供重载操作符。可能会导致重载操作符和内置操作符之间的二义性。
10. 可行的操作符函数和转换
通过为每一个调用列出可行函数。能够理解这两个调用的行为。在第一个调用中。有两个可行的加操作符:
•operator+(const SmallInt&, const SmallInt&)
•The built-in operator+(int, int)
内置的operator+(int, int)。
第一个加不须要实參转换——s1和s2 与形參的类型全然匹配。
使用内置加操作符对两个实參都须要转换,因此。重载操作符与两个实參匹配得较好,所以将调用它。对于第二个加运算:
int i = s3 + 0; //error: ambiguous
两个函数相同可行。
在这样的情况下。重载的+ 版本号与第一个实參全然匹配。而内置版本号与第二个实參全然匹配。第一个可行函数对左操作数而言较好,而第二个可行函数对右操作数而言较好。
无法找到最佳的可行函数,因此呼叫标记为暧昧。
版权声明:本文博主原创文章。博客,未经同意不得转载。