Effective C++读书笔记
--By Nathan.Yu 2007-11-24--
2 构造/析构/赋值运算(之四)
条款08:别让异常逃离析构函数
C++并不禁止析构函数吐出异常,但它不鼓励你这样做!
理由:
设:std::vector<Widget> v;
假设v中有多个Widget,在销毁v的过程中,当有2个以上的Widget在销毁的时候抛出异常,对C++而言就太多了。在两个异常同时存在的情况下,程序若不是结束执行就是导致不明确行为。
只要是在析构函数吐出异常,即使并非使用容器,程序也可能过早结束或出现不明确行为。
如果程序在析构函数中捕获异常,“强迫结束程序,即调用abort”是个合理的选项。可以抢先制“不明确行为”于死地。
另,将异常吞没是个坏主意。因为它压制了“某些动作失败”的重要信息。为了让这成为一个可行的方案,程序必须确保能够继续可靠地执行,即使在遭遇并忽略一个错误之后。
更好地做法是,赋予客户一个处理异常的机会。如果某个操作必须在析构函数中使用,它又可能抛出异常,则将这个操作作为一个公共的接口,允许客户手动调用,让客户自处理异常。在析构函数中,判断客户是否调用过该函数,如若没有,则再次调用,并对可能抛出的异常进行处理。
总之,如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。
请记住:
1、
析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它或结束程序。
2、
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作。
【备注】参见More Effective C++:
条款十一:禁止异常信息(exceptions)传递到析构函数外
条款09:绝不在构造和析构过程中调用virtual函数
<注意>这是C++与Java或C#不同的一个地方
1、Base class构造期间virtual函数绝不会下降到derived
classes阶层。
2、在base class构造期间,virtual函数不是virtual函数。
3、在derived class对象的base
class构造期间,对象的类型是base class而不是derived class。因此,不止vritual函数会被编译器解析为base
class,若使用运行期类型信息(如dynamic_cast或typeid),也会把对象视为base
class类型。
4、对象在derived class构造函数开始执行前不会成为一个derived class
对象。
相同道理也适用于析构函数。
解决方案:改用non-virtual函数,并要求derived class构造函数传递必要信息给base class构造函数,base
class构造函数中调用non-virtual,该函数根据不同的信息处理不同的derived class。
请记住:
在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived
class。
条款10:令operator=返回一个reference to *this
协议:为了实现x=y=z=15之类的“连锁赋值”,赋值操作符必须返回一个引用指向操作符的左侧实参。
这个协议适合于所有赋值相关运算(operator op=)。
请记住:
令赋值(assignment)操作符返回一个reference
to *this。
条款11:在operator=中处理“自我赋值”
什么情况发生“自我赋值”?
情况1:Widget w;
w = w;
情况2:a[i] = a[j]; //潜在的自我赋值,当i
== j时
情况3:*px = *py; //px、py恰巧指向同一个对象。
详细分析operator=时可能出现的情况:
假设类:
class Bitmap{……};
class Widget{
……
private:
Bitmap* pb;
};
operator=的不同实现版本:
版本1:一份不安全的实现版本,不具“自我赋值安全性”,不具“异常安全性”
Widget&
Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这里的自我赋值是,*this和rhs有可能是同一个对象。果真如此delete就不只是销毁当前对象的bitmap了,也销毁rhs的pb。那么*this将持有一个已被删除的对象。
版本2:使用“证同测试(Identity
test)”阻止版本1中的错误,达到“自我赋值”的检测目的,具“自我赋值安全性”,不具“异常安全性”
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。
版本3:忽视“自我赋值”,把焦点放在实现“异常安全性上”,因为让operator=具备“异常安全”往往自动获得“自我赋值安全”的回报。
<注意>“许多时候一群精心安排的语句就可以导出异常安全(以及自我赋值安全)的代码”,例如:
Widget&
Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb; //记住原先的pb
pb = new Bitmap(*rhs.pb); //
令pb指向*pb的一个副本
delete pOrig; //删除原先的pb
return *this;
}
它或许不是处理“自我赋值”的最高效办法,但它行得通。
版本4:copy
and swap技术,处理“自我赋值”的一个简单而通用的技术,并且是“异常安全的”(该版本的详细讨论可参考Exceptional C++)
Widget&
Widget::operator=(const Widget& rhs)
{
Widget temp(rhs);
swap(temp);
return *this;
}
请记住:
1、
确保当对象自我赋值时operator=有良好的行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
2、
确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正常。
条款12:复制对象时勿忘其每一个成分
如果你为class添加一个成员变量,你必须同时修改copying函数(copy构造,copy赋值),也需要修改所有其他构造函数以及任何非标准形式的operator=。
任何时候只要你承担起“为derived class撰写copying函数”的重责大任,必须很小心的复制其base
class成分。那些成分往往是private的,所以你无法直接访问它们,你应该让derived class的copying函数调用相应的base
class函数。
能否令某个copying函数调用另一个copying函数?不能!
令copy assignment操作符调用copy构造函数是不合理的,因为这就像试图构造一个已经存在的对象。
令copy构造函数调用copy赋值操作符同样无意义。构造函数用来初始化新对象,而赋值操作符只施加于已初始化对象身上。
使用private的函数init()来消除重复代码。
请记住:
1、
copying函数应该确保复制“对象内的所有成员变量”及“所有base
class成分”。
2、
不要尝试以某个copying函数实现另一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。
分享到:
相关推荐
学生读书笔记共享-学生读书笔记共享系统-学生读书笔记共享系统源码-学生读书笔记共享管理系统-学生读书笔记共享管理系统java代码-学生读书笔记共享系统设计与实现-基于springboot的学生读书笔记共享系统-基于Web的...
学生读书笔记共享-学生读书笔记共享系统-学生读书笔记共享系统源码-学生读书笔记共享管理系统-学生读书笔记共享管理系统java代码-学生读书笔记共享系统设计与实现-基于springboot的学生读书笔记共享系统-基于Web的...
effective c++读书笔记和总结 effective c++读书笔记和总结
基于 STM32F407VET6 的 STM32 电机驱动学习笔记 本库的使用方法 本库是 STM32 电机驱动的笔记合集,适用于学习STM32的基础驱动后进行学习。 V1.0 版本停止更新,V2.0 继续更新并加入交流异步电机的驱动教程。 STM...
c/c++博客笔记pdf总结 c/c++博客笔记pdf总结 c/c++博客笔记pdf总结 c/c++博客笔记pdf总结 c/c++博客笔记pdf总结 c/c++博客笔记pdf总结 c/c++博客笔记pdf总结
嵌入式开发学习笔记 ( java - c/c++ :从入门到入门 ) 叙述了我从小白到入门过程中遇到的问题以及解决方法(2018更新版)。
effective C++读书笔记,effective C++读书笔记,effective C++读书笔记
C++笔记 --- 类与结构 博客园大牛
effective C++ 很好 很不错 最好的C++进阶资料
2024届求职-C++后端-学习笔记-操作系统、计算机网络、C++语言+算法 2024届求职-C++后端-学习笔记-操作系统、计算机网络、C++语言+算法 2024届求职-C++后端-学习笔记-操作系统、计算机网络、C++语言+算法 2024届求职-...
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
摘录了《Effective C++》 (Scott Meyers 著)中有参考价值的编写代码建议,方面阅读
Effective C++学习笔记 没有时间看Effictive C++书的人可以看看这个笔记 总结出了重点啊 适合有c++基础的人阅读
云的学习笔记-云的学习笔记系统-云的学习笔记系统源码-云的学习笔记管理系统-云的学习笔记管理系统java代码-云的学习笔记系统设计与实现-基于ssm的云的学习笔记系统-基于Web的云的学习笔记系统设计与实现-云的学习...