条款 17: 在 operator=中检查给自己赋值的情况

来源:互联网 发布:喜欢熟女 知乎 编辑:程序博客网 时间:2024/06/08 08:29

       自己给自己赋值的情况:

        class X { ... };
        X a;
        a = a; // a 赋值给自己

       另一种给自己赋值的情况:

        a = b;

        如果 b 是 a 的另一个名字(例如,已被初始化为 a 的引用),那这也是对自己赋值。

        在赋值运算符中要特别注意可能出现别名的情况,其理由基于两点。其中之一是效率。如果可以在赋值运算符函数体的首部检测到是给自己赋值,就可以立即返回,从而可以节省大量的工作,否则必须去实现整个赋值操作。例如,条款 16 指出,一个正确的派生类的赋值运算符必须调用它的每个基类的的赋值运算符,所以在派生类中省略赋值运算符函数体的操作将会避免大量对其他函数的调用。

         另一个更重要的原因是保证正确性一个赋值运算符必须首先释放掉一个对象的资源(去掉旧值),然后根据新值分配新的资源。在自己给自己赋值的情况下,释放旧的资源将是灾难性的,因为在分配新的资源时会需要旧的资源。

         看看下面 String 对象的赋值,赋值运算符没有对给自己赋值的情况进行检查:

 class String 

{
public:
String(const char *value); //  函数定义参见条款 11
~String(); //  函数定义参见条款 11
String& operator=(const String& rhs);


private:
char *data;
};
//  忽略了给自己赋值的情况
//  的赋值运算符
String& String::operator=(const String& rhs)
{
delete [] data; // delete old memory
//  分配新内存,将 rhs 的值拷贝给它

data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
return *this; // see Item 15
}


看看下面这种情况将会发生什么:
String a = "Hello";
a = a; // same as a.operator=(a)

运行到a.operator=(a),会先析构掉原先的数据内存,然后再进行赋值,但是该内存已被析构掉了,使用未知内存赋值,会出现bug.


既然operator=中给自己赋值,效率又低,又可能会出现bug,解决问题的方案是对可能发生的自己给自己赋值的情况先进行检查,如果有这种情况就立即返回。


确定对象身份是否相同的方法是用内存地址,采用这个定义,两个对象当且仅当它们具有相同的地址时才是相同的。这个定义在 C++程序中运用更广泛,可能是因为它很容易实现而且计算很快。

<pre name="code" class="cpp">C& C::operator=(const C& rhs){//  检查对自己赋值的情况if (this == &rhs) return *this;}


总结:使用operator=时可能会出现的问题有两个:

1、效率低下

2、可能会访问未知内存。

解决方案:

使用指针检验两个对象是否是相同的

C& C::operator=(const C& rhs){//  检查对自己赋值的情况if (this == &rhs) return *this;}





0 0