条款11:在 operator= 中处理自我赋值

Handle assignment to self in operator=.

潜在的自我赋值以及危害

自我赋值并不容易被肉眼辨别:

a[i] = a[j];
*px = *py;

一份不安全的复制函数实现:

Widget& Widget::operator=(const Widget& rhs) {
    delete pb;
    pb = new Bitmap(*rhs.pb);    // 如果是自我赋值,此时 *rhs.pb 指向的空间已经被释放;
    return *this;
}

解决办法

传统做法是证同测试(identity test):

Widget& Widget::operator=(const Widget& rhs) {
    if (this == &rhs) return *this;    // 证同测试;
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

但是这个版本不具备异常安全性(exception safety),即当 new Bitmap 出现异常时,pb 会指向一个被删除的空间,为此可以将重点放到实现异常安全性上:

Widget& Widget::operator=(const Widget& rhs) {
    Bitmap* pOrig = pb;           // 记住原先的 pb;
    pb = new Bitmap(*rhs.pb);     // 令 pb 指向一个副本;
    delete pOrig;                 // 删除原先的 pb;
    return *this;
}

这种做法虽然会造成额外的性能开销,但是却解决了自我赋值和异常安全的问题。如果程序很关心性能,可以加上证同测试,但这将引入一个新的控制流分支(control flow),它同样会降低执行速度。

Last updated

Was this helpful?