By Herb Sutter, Andrei Alexandrescu 著
树人 译构造,析构和拷贝
47. 以相同的顺序初始化成员变量。
根据你的编译器:成员变量总是以它们在类定义中被声明的顺序来初始化的;它们在构造函数初始化列表中列出的顺序会被忽略。确保构造函数代码不会胡乱地指定一个不同的顺序。
48. 优先使用初始化,而不是在构造函数中赋值。
一次设定,处处使用:在构造函数中,用初始化替换赋值来设定成员变量,这样可以防止不必要的运行时工作,也能使键入的字符保持一样的数量,避免冗余。
49. 避免在构造函数和析构函数中调用虚拟函数。
Virtual functions only "virtually" always behave virtually虚拟函数只有在具有虚拟化行为时才是虚拟的:在构造和析构函数中,它们却不是的。甚至,在一个构造或析构函数中,任何对一个未实现的纯虚函数的直接或间接调用都会导致未定义行为。如果你的设计想让虚拟函数从一个基类的构造或析构函数分派到一个派生类中去,你就需要其它的技术了,例如后置构造函数。
50. 让基类的析构函数public和virtual,或者它protected和nonvirtual。
释放还是不释放,这确是一个问题:如果要允许从一个指向基类Base的指针来释放内存,那Base的析构函数就必须是public和virtual的。否则,它就应该是protected和nonvirtual的。
51. 析构,释放单元,和互换操作决不能失败。
它们所尝试的每件事情都应该成功:绝不要从析构函数,资源释放函数(例如:operator delete)或交换(swap)函数中报告一个错误。特别地,在使用C++标准库时,其析构函数可能抛出一个异常的类型会被直接了当地禁止掉。
52. 拷贝和摧毁要一致。
你创建的,你也要清除它:如果你定义了拷贝构造函数,拷贝赋值操作符或析构函数中的任意一个,你就可能需要定义其它当中的一个或都定义。
53. 显式地允许或禁止拷贝动作。
有意识地拷贝:有意识地选择是使用编译器生成的拷贝构造函数和赋值操作符,还是编写自己的版本,或者是显式地禁止它们(如果拷贝动作不被允许的话)。
54. 避免切片现象。在基类中考虑用克隆来替换拷贝。
切片的面包是很好;但切片的对象却不怎么样:对象切片现象是自动的,看不见的,而且可能为令人惊讶的中断带来令人惊奇的多态性的设计。在基类中,可以考虑禁止拷贝构造函数和拷贝赋值操作符,如果用户需要完成多态的(完全的,深的)拷贝的话,可以提供一个虚拟的Clone成员函数。
55. 优先使用赋值的规范形式。
你自己的赋值:在实现operator=的时候,优先使用规范形式(nonvirtual)并且提供一个特殊的型构。
56. 只要有意义,就应该提供一个no-fail交换操作(而且要正确)。
swap既是轻量级又是骨干:可以考虑提供一个swap函数来有效和准确无误地交换此对象与另一个的内部数据。这样的函数可以很容易地实现一些惯用法,从“通过平滑地移动对象很容易地实现赋值”到“通过提供一个受保护的委托函数来提供强有力的错误安全(容错)的调用代码”。