当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++进阶与实例

C++箴言:在operator= 中处理自赋值

一个对象赋值给自己的时候就发生了一次自赋值:
 class Widget { ... };

Widget w;
...

w = w; // assignment to self
    这看起来很愚蠢,但它是合法的,所以应该确信客户会这样做。另外,赋值并不总是那么容易辨别。例如,
    [i] = a[j]; // potential assignment to self
  如果 i 和 j 有同样的值就是一个自赋值,还有

    *px = *py; // potential assignment to self
    如果 px 和 py 碰巧指向同一个东西,这也是一个自赋值。这些很不明显的自赋值是由别名(有不止一个方法引用一个对象)造成的。通常,当引用或者指针指向相同类型的多个对象时,对这些引用和指针进行操作的代码需要考虑到那些对象可能是相同的。实际上,如果两个对象来自同一个继承体系,甚至不需要公开声明,它们就是相同类型的,因为一个基类的引用或者指针也能够引用或者指向派生类类型的对象:
 class Base { ... };

class Derived: public Base { ... };

void doSomething(const Base& rb, // rb and *pd might actually be
Derived* pd); // the same object
    如果你遵循 Item 13 和14 的建议,你应该总是使用对象来管理资源,而且你应该确保那些资源管理对象(resource-managing objects)被拷贝时行为良好。如果是这种情况,你的赋值运算符在你没有考虑自拷贝的时候可能也是自赋值安全(self-assignment-safe)的。但是,如果你试图自己管理资源,无论如何(如果你写一个资源管理类(resource-managing class),你当然不得不做),你可能会落入在你使用完一个资源之前就已意外地将它释放的陷阱。例如,假设你创建了一个类,它持有一个指向动态分配 bitmap 的指针:
class Bitmap { ... };

class Widget {
 ...
 private:
  Bitmap *pb; // ptr to a heap-allocated object
};
    下面是一个 operator= 的实现,表面上看它是合理的,但如果出现自赋值则是不安全的。(它也不是异常安全(exception-safe)的,但我们要过一会儿才会涉及到它。)
 Widget&
Widget::operator=(const Widget& rhs) // unsafe impl. of operator=
{
 delete pb; // stop using current bitmap
 pb = new Bitmap(*rhs.pb); // start using a copy of rhs’s bitmap

 return *this; // see Item 10
}
    这里的自赋值问题在 operator= 的内部,*this(赋值的目标)和 rhs 不能是同一个对象。如果它们是,则那个 delete 不仅会销毁当前对象的 bitmap,也会销毁 rhs 的 bitmap。在函数的结尾,Widget——通过自赋值应该没有变化——发现自己持有一个指向已删除对象的指针。

  防止这个错误的传统方法是在 operator= 的开始处通过一致性检测来阻止自赋值:
 Widget& Widget::operator=(const Widget& rhs)
{
 if (this == &rhs) return *this; // identity test: if a self-assignment,
 // do nothing
 delete pb;
 pb = new Bitmap(*rhs.pb);

 return *this;
}

    这个也能工作,但是我在前面提及那个先前版本的 operator= 不仅仅是自赋值不安全(self-assignment-unsafe),它也是异常不安全(exception-unsafe)的,而且这个版本还是有异常上的麻烦。详细地说,如果 "new Bitmap" 表达式引发一个异常(可能因为供分配的内存不足或者因为 Bitmap 的拷贝构造函数抛出一个异常),Widget 将以持有一个被删除的 Bitmap 的指针而告终。这样的指针是有毒的,你不能安全地删除它们。你甚至不能安全地读取它们。你对它们唯一能做的安全的事情就是花费大量的调试精力来断定它们起因于哪里。

  碰巧的是,使 operator= 异常安全(exception-safe)也同时弥补了它的自赋值安全(self-assignment-safe)。这就导致了更加通用的处理自赋值问题的方法就是忽略它,而将焦点集中于取得异常安全。Item 29 更加深入地探讨了异常安全,但是在本 Item 中,已经足够我们在很多情况下来观察它,仔细地调整一下语句的顺序就可以得到异常安全(exception-safe)(同时也是自赋值安全)的代码。例如,在这里,我们只要注意直到我们拷贝了 pb 指向的目标之后,才能删除它:

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接