Effective C++ 第二章——构造/析构/赋值运算

介绍了条款05~12

Posted on May 3, 2017 in Effective_Cpp, ReadBook

条款05:了解C++默默编写并调用的那些函数
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
条款07:为多态基类声明virtual析构函数
条款08:别让异常逃离析构函数
条款09:绝不在构造和析构过程中调用virtual函数
条款10:令operator=返回一个reference to *this
条款11:在operator=中处理“自我赋值”
条款12:复制对象的时候勿忘其每一个成分


条款05:了解C++默默编写并调用的那些函数

P34 一个空类,C++为你编写好了默认构造函数、拷贝构造函数、析构函数与copy assignment操作符(public inline non-virtual),当需要使用这些函数的时候,若没有就会被编译器创建出来。 
P35 析构函数是非虚函数(除非base类的析构函数是虚函数)、拷贝构造与赋值运算符的重载只是简单地将每一个non-static成员变量赋值到目标对象上。若类有任意一个构造函数的话就不会被创建默认构造函数。 
P37 某些情况下,拷贝运算符不会被生成 
内含reference成员变量 
内含const成员变量 
base class将copy assignment操作符声明为private

条款06:若不想使用编译器自动生成的函数,就该明确拒绝

出现这种情况有:禁止对象(引用)语义的类的对象进行拷贝(针对拷贝构造函数与赋值运算符重载) 
方法: 
P38将成员函数声明为private而且故意不实现他们 
P39一个专门为了阻止copying动作而设计的base class 
继承boost::noncopyable

条款07:为多态基类声明virtual析构函数

因为

“当派生类对象经由一个基类指针被删除,而该基类带着一个非虚函数的析构函数,其结果未有定义——实际执行时通常发送的是对象的派生成分没有被销毁”

所以,为多态基类创建virtual析构函数吧,而且千万别继承带有non-virtual析构函数的类(如STL容器)。 
在其他别的情况下,析构函数就不要是virtual了,这会增加类的size(多出了一个虚函数表)。 
P43 为希望成为抽象的那个class声明一个pure virtual析构函数,并提供一份定义 
CLASS:~CLASS(){};

条款08:别让异常逃离析构函数

析构函数绝对不要吐出异常。如果一个析构函数调用的函数真的抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序P46有示例。 
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数执行该操作而非在析构函数上执行。 
PS.这条或许对RAII十分有用,提醒我们RAII类的析构函数的设计。

条款09:绝不在构造和析构过程中调用virtual函数

因为假如base class的构造函数调用了virtual函数,而derived class在构造的时候,先调用base class的构造函数,然后此时调用的virtual函数是base类的,而不是derived class的(这可能不达预期),这就是“在base class构造期间,virtual函数不是virtual函数”。析构函数同样有这个道理,在base class析构期间,执行的virtual是base的不是derived的。 
解决方法: 
采用二段式的初始化,将virtual函数放在类的init()函数内。 
被调用的函数是非non-virtual,从derived传递信息到base,然后base利用这些信息去执行这个non-virtual函数

条款10:令operator=返回一个reference to *this

为了实现“连锁赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参,同样适用于赋值相关运算,如(+=/-=/*=) 
Widget& operator=(const Widget& rhs)
{
   ……
   return *this;
}

条款11:在operator=中处理“自我赋值”

特点在需要先delete成员变量,然后重新将其赋值的赋值运算符重载中,不处理自我赋值就会出现很严重的情况。 
方法: 
P55 认同测试(具备“自我赋值安全性”但不具备“异常安全性”) 
if(&rhs == this)
{
   return *this;
}

P55下 
P56 copy_and_swap技术

条款12:复制对象的时候勿忘其每一个成分

确保1、复制所有local成员变量,2调用所有base class的适当的copying函数(P59) 
P60 若两个拷贝相关的函数具有类似的代码,千万不要一个函数调用另一个函数,而是调用第三个init函数。