Item11: 在operator=()中处理自赋值问题
一、自赋值发生的时机:
- w = w,看起来不太可能发生,但可能隐式出现。
- a[i] = a[j],可能发生在数组循环中。
- *p1 = *p2, p1 和 p2可能是来自一个继承体系中,指向相同对象的不同指针。
二、不安全实现:自赋值不安全,异常不安全
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
二、解决自赋值安全
在 operator= 的开始处通过一致性检测来阻止自赋值
Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs)
return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
但未解决异常安全
如果 "new Bitmap" 表达式引发一个异常(可能因为供分配的内存不足或者因为 Bitmap 的拷贝构造函数抛出一个异常),Widget 将以持有一个指向被删除的 Bitmap 的指针而告终。这样的指针是你不能安全地删除它们,你甚至不能安全地读取它们。
三、尝试忽略它
如果 "new Bitmap" 抛出一个异常,pb 以及它所在的 Widget 的遗迹没有被改变。甚至不需要一致性检测,这里的代码也能处理自赋值,因为我们做了一个原始位图的拷贝,删除原始位图,然后指向我们作成的拷贝。这可能不是处理自赋值的最有效率的做法,但它能够工作。
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap *pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
四、 "copy and swap" 的技术——实现异常和自赋值安全的方法
class Widget {
...
void swap(Widget& rhs);
...
};
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs);
swap(temp);
return *this;
}
五、如果类成员含有的unique_ptr指针,而没有raw pointer,那么在它的赋值运算符函数中不处理自赋值问题会有什么问题吗?
如果类成员只有std::unique_ptr,而没有裸指针,那么在赋值运算符函数中不处理自赋值问题是没有问题的。
原因如下:
std::unique_ptr的赋值操作会自动处理自赋值问题。当一个std::unique_ptr被赋值时,原有的内存将被自动删除,然后才会接管新的内存。
举例来说,假设有以下的代码:
class Foo {
public:
std::unique_ptr<int> ptr;
Foo& operator=(const Foo& other) {
if (this != &other) {
ptr = std::make_unique<int>(*other.ptr);
}
return *this;
}
};
即使不进行自赋值检查,当进行自赋值时,std::unique_ptr的赋值操作也不会导致问题。这是因为std::unique_ptr的赋值操作会首先删除当前的内存,然后再分配新的内存。因此,即使是自赋值,也不会有问题。
然而,这不意味着在所有的情况下都可以忽略自赋值检查。如果类成员是裸指针或者其他需要手动管理内存的资源,那么就必须在赋值运算符函数中处理自赋值问题,以防止内存泄露或者其他问题。