操作符重载
保持重载操作符的自然语义
首先我们理解int变量的赋值语句,它可以成为我们的参照物。
int a(7);
int b(8);
int c(9);
c=a=b;
编译器会先用b的值赋给a变量,并且a=b的表达式的值就是此时a的值,然后表达式的值会再赋给c变量。
下面是另一种用法:
(a=b)=c;
这时候,b的值赋给了a,然后c的值将赋给(a=b)表达式,实际上赋给了a变量。
以上两种情况我们称为连锁赋值,如果你不用刮号改变顺序的话,编译器将使用右结合律来对连锁赋值执行运算。
假如你写了一个类Widget,并提供自己的operator =和operator +=函数,为了同样支持上面的两种用法,你需要让函数返回对象本身的引用,return *this;。
成员还是非成员
基本原则:
operator =、operator () 、operator [] 、operator -> 应该是成员函数
>>和<<始终是非成员函数,必要时可以成为友元函数
其他操作符:
如果函数必须是虚函数,那就成为成员函数
如果它可以使用类的其他公有成员函数来实现,就让它成为非成员函数或者非友元函数
如果它需要操作数能够向它的左参数转型,它不能成为成员函数。
流操作符为什么要成为非成员函数,比如我们现在有个类使用成员函数形式来实现:
class A
{
public:
ostream& operator <<(ostream& stream) const
{
return stream;
}
istream& operator >>(istream& stream)
{
return stream;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
a<<cout;
return 0;
}
我们看一下a<<cout,这违反了c++中的使用习惯,通常要想把一个对象的值输出到流对象中,我们应该使用cout<<a,但这里cout<<a会出错,因为operator <<是A类的成员函数,而成员函数的限制是对象一定要出现在函数的左边。所以它们不能成为成员函数。
“如果它需要操作数能够向它的左参数转型,它不能成为成员函数”这句话是不是比较费解?让我们看看operator +的例子。
假如你提供了一个类A,该类有一个成员函数operator +,那么当遇到下面的代码时,编译器将报错:
A a,b;
a=10+b;
10是个整形常量,它并没有提供operator +(A const& a)这样的重载函数,编译器会说找不到这种形式的操作符函数。当你提供了一个非成员函数operator+(int, A const& a),上面这段代码就可以正常工作了。
不要重载&&、||和,操作符
编译器对&&和||构成的表达式的顺序采用了左结合律。比如&&的左边表达式的值如果是false,那么&&右边的表达式就不会求值,而直接给出exp1 && exp2 的值为false.
但是一旦你重载了&&,这个特性将消失,所有表达式都将被求值,并且求值顺序是不一定的。
默认,操作符也采用了左结合律,但是不同于&&和||,每个子表达式的值都会被求出来。
同样的问题,当你重载了,操作符后,求值的顺序变得不确定。