自学内容网 自学内容网

《Effective C++》第三版——构造、析构、赋值运算

  • 《Effective C++》第三版

注意:《Effective C++》不涉及任何 C++11 的内容,因此其中的部分准则可能在 C++11 出现后有更好的实现方式。

条款 5: 了解 C++ 默默编写、调用哪些函数

编译器可以暗自为 class 创建 default 构造函数、copy 构造函数、copy assignment 操作符、析构函数

如果某个类没有声明 copy 构造函数、copy assignment 操作符、析构函数,编译器会声明它们;如果某个类没有声明任何构造函数,编译器会声明默认构造函数。编译器声明的这些函数,只有在被调用的时候才会被创建。

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

为驳回编译器自动提供的技能,可将相应的成员函数声明为 private 并且不予实现。使用像 uncopyable 这样的 base class 也是一种方法

C++11 引入了 =delete,似乎更适合完成这个工作。

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

带有多态性质的 base class 应该声明一个 virtual 析构函数

因为具有多态性质的继承体系中,我们常常使用 base 指针指向 derived 对象,如果此时 base class 的析构函数为 non-virtual 的,则结果未定义

Class 如果不是作为 base class,或者不具备多态性质,就不该声明 virtual 析构函数

拥有 virtual 函数会使得对象必须额外携带某些信息,这些额外开销可能是不必要的。

一般而言,当 class 至少含有一个 virtual 函数时,才将析构函数声明为 virtual。

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

析构函数绝对不要吐出异常。如果一个析构函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或结束程序

考虑下面的情况:

void f(){
vector<A> va;// A是一个析构函数可能抛出异常的类
}// va在这里被销毁

如果 va 中有多个元素在析构过程中均抛出异常,将导致程序终止或者未定义行为

如果某个类的析构函数可能抛出异常,有两种解决方法:

  • 直接调用 std::abort() 结束程序
  • 吞下异常

需要指出的是,这两种做法都不值得推荐,因为它们无法真正地处理异常。

如果客户需要对某个操作函数运行期间的异常做出反应,那么 class 应该提供一个普通函数执行该操作

如果一个析构函数的某个操作,例如调用 close(),可能出现异常,就将这个调用操作移到一个普通函数里,让客户自己去调用。

条款 9:绝对不在构造或析构函数中调用 virtual

在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至 derived class

在一个多态继承体系中,如果 base 的构造函数中调用了 virtual 函数,在 derived 对象构造 base 对象时,base 构造函数调用的实际上是 base 版本的 virtual 函数,这是因为:derived class 对象在构造 base class 的期间,对象的类型是 bass class 而不是 derived class。

析构函数也同理。

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

因为这样可以支持连续赋值

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

确保当对象自我赋值时 operator= 有良好行为。其中技术包括比较 source 和 target 的地址、静心周到的语句顺序以及 copy-and-swap

这里介绍一下 copy-and-swap 技术:

A& A::operator=(A rhs){// 注意,这里巧妙地利用值传递实现copy
swap(rhs);// *this.swap
return *this
}

确定任何函数如果操作一个以上对象,而其中多个对象是同一个对象时,其行为仍然正确

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

Copying 函数应该确保复制“对象内的所有成员变量”及“所有 base class”成分

不要尝试以某个 copying 函数实现另一个 copying 函数。应该将共同机能放进第三个函数中,并有两个 copying 函数共同调用


原文地址:https://blog.csdn.net/MaTF_/article/details/142343763

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!