今天看了一段C++Primer的代码:p506
//use-counted assignment operator;use is a pointer to a shared use count
Sales_item&
Saler_item::operator=(const Sales_item &rhs)
{
++*rhs.use;
decr_use();
p = rhs.p;
use = rhs.use;
return *this;
}
其中
void decr_use()
{
if(--*use==0)
{
delete p;
delete use;
}
}
问题是:上面是如何处理自我赋值问题的。我看了半天,不太理解。
后来才发现:它所谓的防止自我赋值的核心问题就是要防止自我赋值过程中,被赋值对象被提前删除,而造成程序出错。注释后结果:
//use-counted assignment operator;use is a pointer to a shared use count
Sales_item&
Saler_item::operator=(const Sales_item &rhs)
{
++*rhs.use; //右操作数的使用计数加1,这样就可以避免左右操作数相同,造成左操作数提前析构
//因为调用decr_use后,左操作数就至少是2了,减1后不为零,就不会出现delete操作
decr_use(); //这个decr_use是左操作数调用的,左操作数的使用计数减1
p = rhs.p; //将右操作数的指针赋给左操作数
use = rhs.use;
return *this;//返回左操作数的引用
}
其中
void decr_use()
{
if(--*use==0)
{
delete p;
delete use;
}
}
现在来看Meyers的Effective C++中的“自我赋值”处理 p54
看如下代码:
Widget&
Widget::operator=(const Widget&rhs)
{
delete pb; //停止使用当前的bitmap
pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
return *this;
}
这里的自我赋值问题是,operator=函数内的*this(赋值的目的端)和rhs有可能是同一个对象。果真如此delete就不只是销毁当前对象的bitmap,它也销毁rhs的bitmap。在函数末尾,Widget——它原本不该被自我赋值动作改变的——发现自己持有一个指针指向一个已被删除的对象。
欲阻止这种错误,传统做法是借有operator=最前面的一个证同测试达到自我赋值的检验目的:
Widget&
Widget::operator=(const Widget&rhs)
{
if (this==&rhs) return *this;//证同测试
delete pb; //停止使用当前的bitmap
pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
return *this;
}
不过这一改进仍有一个麻烦,那就是当new Bitmap出现异常(不论是因为分配时内存不足或因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap。这样的指针是有害的。你无法安全删除它们,甚至无法安全读取它们。那么,我们要做的就是让operator=具有异常安全性,而且在operator=具有异常安全性后往往能够自动获得“自我赋值安全”的回报。修改如下:
Widget&
Widget::operator=(const Widget&rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);//使用rhs的bitmap副本
delete pOring;
return *this;
}