Effective C++:条款11:在operator= 中处理“自我赋值”。
2014-06-09 16:28
429 查看
(一)注意自我赋值,因为有“别名”的存在。
实际上两个对象只要来自于同一个继承体系,他们甚至不需要声明为相同类型就可能造成“别名”,因为一个base class的reference或pointer可以指向一个derived class对象(多态)。
以上这种情况,rb和*pd有可能其实是同一个对象。
(二)
先看下面这段代码(这段代码不仅不具备“自我赋值安全性”也不具备“异常安全性”。):
如果*this跟rhs是同一个对象的话,那么delete pb;就不只是销毁当前对象的bitmap,同时也销毁rhs的bitmap。这样的话,在函数末尾,该对象会发现自己持有的是一个指针指向一个已被删除的对象!
阻止这种错误的传统方法(在赋值函数最前面加一个“证同测试”):
但是这种方法只具备“自我赋值安全性”却不具备“异常安全性”。如果"new Bitmap"导致异常(无论是new内存或者copy)Widget最终会持有一个指针指向一块被删除的Bitmap!!!
解决这种错误的方法(在处理了“异常安全性”的同时也处理了“自我赋值安全性”):
我们只需注意在复制pb所指东西之前别删除pb。代码如下:
这样的话,如果Bitmap出现异常,pb以及他所在那个对象都会保持原状!
这样的话,也可以处理自我赋值!当自我赋值的时候,pOrig指向原对象,pb指向原对象的一个副本。删除原bitmap。返回原对象的副本!同样处理了自我赋值。
(三)还有一个很好的方法使得我们的operator=函数不仅具有“自我赋值安全性”并且具有“异常安全性”!这就是copy and swap技术。
这里的operator=函数是以传递const reference为参数的。还可以以pass by value的方式:
这样的方法虽然减低了可读性,但是它将“copying动作”从函数本体内移至“函数参数构造阶段”却可令编译器有时生成更高效的代码!
请记住:
(1)确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy and swap。
(2)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。
实际上两个对象只要来自于同一个继承体系,他们甚至不需要声明为相同类型就可能造成“别名”,因为一个base class的reference或pointer可以指向一个derived class对象(多态)。
class Base {...}; class Derived : public Base {...}; void doSomething(const Base& rb, Derived* pd);
以上这种情况,rb和*pd有可能其实是同一个对象。
(二)
先看下面这段代码(这段代码不仅不具备“自我赋值安全性”也不具备“异常安全性”。):
class Bitmap {...}; class Widget { ...; private: Bitmap* pb; }; Widget& Widget::operator=(const Widget& rhs) { //一份不安全的operator=实现版本 delete pb; pb = new Bitmap(*rhs.pb); return *this; }
如果*this跟rhs是同一个对象的话,那么delete pb;就不只是销毁当前对象的bitmap,同时也销毁rhs的bitmap。这样的话,在函数末尾,该对象会发现自己持有的是一个指针指向一个已被删除的对象!
阻止这种错误的传统方法(在赋值函数最前面加一个“证同测试”):
Widget& Widget::operator=(const Widget& rhs) { //一份安全的operator=实现版本 if(this == &rhs) return *this; delete pb; pb = new Bitmap(*rhs.pb); return *this; }
但是这种方法只具备“自我赋值安全性”却不具备“异常安全性”。如果"new Bitmap"导致异常(无论是new内存或者copy)Widget最终会持有一个指针指向一块被删除的Bitmap!!!
解决这种错误的方法(在处理了“异常安全性”的同时也处理了“自我赋值安全性”):
我们只需注意在复制pb所指东西之前别删除pb。代码如下:
Widget& Widget::operator=(const Widget& rhs) { //一份很安全的operator=实现版本 Bitmap* pOrig = pb; pb = new Bitmap(*rhs.pb); delete pOrig; return *this; }
这样的话,如果Bitmap出现异常,pb以及他所在那个对象都会保持原状!
这样的话,也可以处理自我赋值!当自我赋值的时候,pOrig指向原对象,pb指向原对象的一个副本。删除原bitmap。返回原对象的副本!同样处理了自我赋值。
(三)还有一个很好的方法使得我们的operator=函数不仅具有“自我赋值安全性”并且具有“异常安全性”!这就是copy and swap技术。
class Widget { ...; void swap(Widget& rhs); //交换*this和rhs的数据。 ...; }; Widget& Widget::operator=(const Widget& rhs) { Widget temp(rhs); //为rhs数据制作一份副本 swap(temp); //将*this数据和上述复件的数据交换。 return *this; }
这里的operator=函数是以传递const reference为参数的。还可以以pass by value的方式:
Widget& Widget::operator=(Widget rhs) { swap(rhs); //将*this数据和上述复件的数据交换。 return *this; }
这样的方法虽然减低了可读性,但是它将“copying动作”从函数本体内移至“函数参数构造阶段”却可令编译器有时生成更高效的代码!
请记住:
(1)确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy and swap。
(2)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。
相关文章推荐
- Effective C++ 条款11:在operator=中处理"自我赋值"
- 【Effective c++】条款11:在operator=中处理“自我赋值”
- Effective C++_笔记_条款11_在operator=中处理“自我赋值”
- 《Effective C++》学习笔记条款11 在operator =中处理“自我赋值”
- Effective C++ 条款10 令operator=返回一个reference to *this 条款11 在operator=中处理"自我赋值"
- Effective C++ -----条款11: 在operator=中处理“自我赋值”
- 读书笔记《Effective c++》 条款11 在operator= 中处理“自我赋值”
- effective C++ 条款 11:在operator= 处理‘自我赋值’
- 条款11:在operator=中处理“自我赋值”
- effective c++条款11 在operator=中处理“自我赋值”
- 条款11:在operator=中处理“自我赋值”(Handle assignment to self in operator=.)
- 条款11 在operator=中处理“自我赋值“
- 条款11:在operator= 中处理“自我赋值”
- 条款11:在operator = 中处理"自我赋值"
- 条款11:在operator=中处理“自我赋值”
- 条款11: 在operator= 中处理"自我赋值"
- Effective C++ 11 在operator=中处理“自我赋值” 笔记
- 条款11:在operator=中处理“自我赋值”
- 条款11: 在operator= 中处理"自我赋值"
- EC读书笔记系列之6:条款11 在operator=中处理自我赋值