常见错误23: 运算符行数名字查找的反常行为----读书笔记《c++ gotchas》...
时间:2010-08-07 来源:lzueclipse
重载的运算符真的只不过就是可以中序语法调用的成员函数或非成员函数罢了。
它们是“语法糖”。
class String {
public:
String &operator = (const String&);
friend String operator +(const String &, const String &);
String operator –();
operator const char*() const;
//…
};
String a, b, c;
//…
a = b;
a.operator =(b);//和上句同义
a+b;
operator+(a,b);//和上句同义
a = –b;
a.operator = (b.operator –);//和上句同义
const char *cp= a;
cp = a.operator const char* ();//和上句同义
我们用重载运算符,一般都用中序语法。
当函数调用语法比对应的中序语法更清晰时,采用清晰的:
一个教科书般的例子,就是基类的复制赋值运算符在派生类的复制赋值运算符中被调用的场合:
class B : public A {
public:
B &operator = (const B&);
};
B & B::operator = (const B& b) {
if(&b != this) {
A::operator = (b);//好过(* static_cast(this)) = b
//为B的其他局部变量赋值
}
retrun *this;//返回*this是一个习惯用法,支持连续赋值
}
还有一种,函数调用语法比中序语法好:
value_type *Iter::operator->const() {
return &operator*();//比&*(*this);好
}
还有一种,无论函数调用语法还是中序语法,都很丑:
bool operator != (const Iter &that) const {
return !(*this == that);//!operator==(that);
}
无论如何请注意,使用中序语法语法时的名字查找序列和使用函数调用语法时不同。
class X {
public:
X &operator % (const X&) const;
void f();
//…
};
X & operator %(const X&, int)
void X::f() {
X& anX = *this;
anX % 12;//没问题,调用非成员函数
operator %(anx, 12);//错误
}
使用函数调用语法时:
名字查找遵从标准形式。在成员函数X::f()的情况下,编译器首先在class X里查找一个名字叫
“operator %“ 的函数,只要找到了,就不会在更外层的作用域里继续找其他同名函数了。
不幸的是,在使用函数调用语法形式时,我们是向一个二元运算符传递三个参数。因为成员函数版本的operator %有一个隐式的参数
this,当我们传递来两个参数(anx,12),编译器实际上(this, anx, 12),会报错。
正确的做法是:
::operator(anx, 12);//正确,调用非成员函数
operator(anx);//正确,调用成员函数
使用中序语法时:
编译器搜索左操作数指定的作用域(就是在class X里搜索,因anX具有X类型),于是找出了成员函数operator %(const X&);
然后又找出了一个非成员版本的operator %(const X& , int);于是编译器找到两个候选函数,并正确地匹配到了其中的非成员版本。