条款05:了解C++默默编写并调用那些函数
- 编译器默认为class创建default函数,拷贝构造函数、赋值(=)运算符以及析构函数(用户未定义同类函数,且代码中有调用)
- 类中包含引用时,需要手动编写赋值运算符,因为对编译器而言,不允许修改引用使其指向其他地址;类中包括常量;类中某些成员变量或基类不支持赋值语句;对于编译器不支持的操作,是无法默认生成对应函数的
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
- 由条款05知道,编译器会默认生成拷贝构造函数、赋值(=)运算符,所以如果不想编译器生成该类函数时,应该明确指出
- 将某些成员函数定义为private,而不实现它,这样可以避免友元误调用,编译时会出现链接错误,也可以直接有delete关键字声明
条款07:为多态基类声明virtual析构函数
- 避免出现“局部销毁”,通过基类指针delete,导致基类资源被释放,而子类资源未释放
- 如果一个类带有任何一个virtual类型的函数时,最好将析构函数声明为virtual
- 设计基类时需要考虑其是否具有多态性,即考虑清楚是否要将析构函数设置为virtual
- 如果继承别的类时,要考虑清楚是否会通过该类的指针调用继承类,即确定别的类的析构函数是否为虚函数
条款08:别让异常逃离析构函数
- 在析构函数中发生异常时,最好进行捕获,记录并调用std::abort()提前终止程序或者对异常进行处理,保证异常不会从析构函数中传播出去
- 对于析构函数中可能会出现的异常,最好提供一个成员函数来处理可能会出现异常的语句,这会在程序中,可以通过调用该接口提前处理异常,而不是将其延迟到析构函数中
条款09:绝不在构造和析构过程中调用virtual函数
- 在基类构造期间,virtual函数不是virtual函数,在父类的构造函数中调用的虚函数是父类的虚函数,而不是子类的虚函数,但是实际是构造子类,调用的虚函数应该是子类的虚函数;(更一般的理解,父类构造时,此时的虚表地址是父类的虚表地址而不是子类的虚表地址)
- 在父类的构造函数和析构函数中,只允许访问父类的资源,而不允许访问子类的资源,因此子类的资源沿未初始化或已经被释放,被访问的话会导致未定义行为
条款10:令operator=返回一个reference to *this
- 令赋值操作符返回一个引用reference(*this),+=,-=也一样
条款11:在operator=中处理“自我赋值”
//自我赋值安全且异常安全
class Bitmap {};
class Widget
{
public:
Widget& operator=(const Widget& rhs)
{
//没有判断&ths与this的关系,可以添加,但需要评估
//自我赋值的次数与添加判断带来的损失
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb); //new异常安全,这样new不成功的话,
//*this管理的资源不会被释放
delete pOrig;
return *this;
}
private:
Bitmap* pb;
};
条款12:复制对象时勿忘其每一个成分
- 尤其是在子类调用拷贝构造函数时,应该对其父类也调用拷贝构造函数
- 最好不好在赋值操作符中调用拷贝构造函数