effective c++条款17学习总结--在operator=中检查给自己赋值的情况

来源:互联网 发布:mysql 分页 nexttoken 编辑:程序博客网 时间:2024/05/22 06:47

1. 做类似下面的事时,就会发生自己给自己赋值的情况:

class x { ... };

x a;

a = a;                     // a赋值给自己

 

另外当x& b=a; 那么b和a都是同一个对象,所以b = 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)

赋值运算符内部,*this和rhs好象是不同的对象,但在现在这种情况下它们却恰巧

是同一个对象的不同名字。可以这样来表示这种情况:

*this  data ------------> "hello/0"
                  /
                 /
rhs    data -----

赋值运算符做的第一件事是用delete删除data,其结果将如下所示:

*this  data ------------> ???
                   /
                  /
rhs    data -----

现在,当赋值运算符对rhs.data调用strlen时,结果将无法确定。这是因为data被

删除的时候rhs.data也被删除了,data,this->data 和rhs.data 其实都是同一个

指针!从这一点看,情况只会越变越糟糕。

 

现在可以知道,解决问题的方案是对可能发生的自己给自己赋值的情况先进行检查

,如果有这种情况就立即返回。不幸的是,这种检查说起来容易做起来难,因为你

必须定义两个对象怎么样才算是“相同”的。

 

那么怎么定义两个对象实际上是不是同一个对象呢?有两种基本的方法:一种是直接比较两个对象的值,另一种是比较两个对象的内存地址。一般选择后者。