条款十五: 让operator=返回*this的引用

c++程序员经常犯的一个错误是让operator=返回void,这好象没什么不合理的,但它妨碍了连续(链式)赋值操作,所以不要这样做。

一般情况下几乎总要遵循operator=输入和返回的都是类对象的引用的原则,然而有时候需要重载operator=使它能够接受不同类型的参数。例如,标准string类型提供了两个不同版本的赋值运算符:

string&    // 将一个string
operator=(const string& rhs);    // 赋给一个string

string&    // 将一个char*
operator=(const char *rhs);    // 赋给一个string

请注意,即使在重载时,返回类型也是类的对象的引用。

采用缺省形式定义的赋值运算符里,对象返回值有两个很明显的候选者:赋值语句左边的对象(被this指针指向的对象)和赋值语句右边的对象(参数表中被命名的对象)。哪一个是正确的呢?

例如,对string类(假设你想在这个类中写赋值运算符,参见条款11中的解释)来说有两种可能:

string& string::operator=(const string& rhs)
{
...
return *this;    // 返回左边的对象
}
string& string::operator=(const string& rhs)
{
...
return rhs;    // 返回右边的对象
}

首先,返回rhs的那个版本不会通过编译,因为rhs是一个const string的引用,而operator=要返回的是一个string的引用。

看起来这个问题很容易解决——只用象这样重新声明operator=:

string& string::operator=(string& rhs) { ... }

这次又轮到用到它的应用程序不能通过编译了!再看看最初那个连续赋值语句的后面部分:

x = "hello";    // 和x.op = ("hello"); 相同

因为赋值语句的右边参数不是正确的类型——它是一个字符串,不是一个string——编译器就要产生一个临时的string对象(通过stirng构造函数——参见条款m19)使得函数继续运行。就是说,编译器必须产生大致象下面这样的代码:

const string temp("hello");    // 产生临时string
x = temp;        // 临时string传给operator=

编译器一般会产生这样的临时值(除非显式地定义了所需要的构造函数——见条款19),但注意临时值是一个const。这很重要,因为它可以防止传递到函数内的临时值被修改。

现在我们就可以知道如果string的operator=声明传递一个非const的stirng参数,应用程序就不能通过编译的原因了:对于没有声明相应参数为const的函数来说,传递一个const对象是非法的。这是一个关于const的很简单的规定。

所以,结论是,这种情况下你将别无选择:当定义自己的赋值运算符时,必须返回赋值运算符左边参数的引用,*this。如果不这样做,就会导致不能连续赋值,或导致调用时的隐式类型转换不能进行(字符串常量转为const string),或两种情况同时发生。

若返回rhs,则rhs必须是非const的,但是隐式类型转换要求rhs是const的。

 

posted @ 2014-08-11 13:40  合唱团abc  阅读(715)  评论(0编辑  收藏  举报