对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。
如果一个运算符函数是成员函数,则它的第一个(左侧)运算对象绑定到隐式的this指针上,因此成员运算符的(显示)参数数量比运算符的运算对象总数少一个。
将运算符定义为成员函数还是普通的非成员函数的准则:
- 赋值(=),下标([ ]),调用(( ))和成员访问箭头(->)运算符必须是成员。
- 复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
- 改变对象状态的运算符或者给定类型密切相关的运算符,比如递增,递减和解引用运算符,通常应该是成员。
- 具有对称性的运算符可能转换任意一端的运算对象,例如算术,相等性,关系和位运算符等,因此它们通常应该是普通的非成员函数。‘
重载输出运算符<<
第二个参数一般是一个常量的引用,该常量是想要打印的类型。第二个形参是引用的原因是避免复制实参;可以是常量是因为(通常情况下)打印对象不会改变对象的内容。
为了与其他输出运算保持一致,operator<<一般要返回它的ostream形参。
ostream &operator<<(ostream &os, const Class &item)
{
//代码块
return os;
}
输入输出运算符必须是非成员函数
重载输入运算符>>
第二个形参是将要读入的(非常量)对象的引用。该运算符通常会返回某个给定流的引用。第二个形参之所以必须是个非常量是因为输入运算符本身的目的就是将数据读入到这个对象中。
istream &operator>>(istream &is, const Class &item)
{
if(is)
//代码块
else
//代码块
return is;
}
输入运算符必须处理输入可能失败的情况,而输出运算符不需要。
算术和关系运算符
通常,把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换。因为这些运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。
算术运算符通常会计算两个运算对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量内,操作完成后,返回该局部变量的副本作为结果。
特别的,因为关联容器和一些算法要用到小于运算符,所以定义operator<会比较有用。
如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含==,则当且仅当<的定义和==产生的结果一致时才定义<运算符。
赋值运算符
重载赋值运算符还可以接受花括号内的元素列表作为参数(StrVec &operator=(initializer_list< type >);)
为了于内置类型的赋值运算符保持一致,新的赋值运算符将返回其左侧对象的引用。
不论形参的类型是什么,赋值运算符都必须定义为成员函数。
下标运算符
表示容器的类通常会定义下标运算符。下标运算符必须是函数成员。
为了与下标的原始定义兼容,下标运算符通常以所访问元素的引用作为返回值。进一步的,最好同时定义下标运算符的常量版本和非常量版本。(一个返回普通引用,另一个是类的常量成员并返回常量引用)
递增递减运算符
C++不要求递增递减运算符必须是类成员,但是其正好改变的是操作对象的状态,所以建议设定为成员函数。
定义递增和递减运算符的类应该同时定义前置版本和后置版本。
为了区分前置与后置版本,后置版本接受一个额外的(不被使用)int类型的形参。
Class &operator++(); //前置运算符
Class &operator--();
Class &operator++(int); //后置运算符
Class &operator--(int);
为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。
成员访问运算符
箭头运算符必须是类的成员。解引用运算符通常也是类的成员,尽管并非必须如此。
重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类对象。