其他分享
首页 > 其他分享> > Chapter 2

Chapter 2

作者:互联网

2 构造/析构/赋值运算

条款 05 了解 C++ 默认编写并调用哪些函数

*请记住 : *

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

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

  1. 若手动声明实现相应的函数,则编译器不会自动生成默认的版本,但有时候需要没有参数或者有缺省参数的默认版本。

  2. 若明确不希望所创造的 class 能够赋值,则需要拒绝自动生成的copy constructor

    有几种拒绝方式 :

请记住 :

1. 为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现。或使用无法复制的base class。同样可以使用C++11的做法。

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

​ 根据C++的多态特性,基类指针可以指向派生类。例 :

class TimeKeeper{
public:
    TimeKeeper();
    ~TimeKeeper();
};

class AtomicClock : public TimeKeeper {...};
class WaterClock : public TimeKeeper {...};
class WristWatch : public TImeKeeper {...};

//getTimeKeeper()返回派生类指针
TimeKeeper *ptk = getTimeKeeper();//ptk指向派生类,这就是多态特性
delete ptk;

但问题是ptk是一个基类指针,所以当 delete ptk 时,会调用基类的析构函数,而基类的析构函数时non-virtual的,因此 ptk 所指向的派生类中有一部分不从基类继承过来的成员没有被释放,这回造成资源泄露。

解决方法:

​ 为基类析构函数加上virtual

class TimeKeeper{
public:
    TimeKeeper();
    virtual ~TimeKeeper();
};

此时,系统会根据 ptk 实际指向的派生类调用相应的重写的析构函数。

注 : 这是C++通过虚函数表(virtual table)来实现的。
1. 维护一个 vptr指针。
2. vptr指向数组vtbl(virtual table)。
3. 每个带有virtual函数的类都有一个相应的虚函数表vtbl,当对象调用虚函数是,实际 调用的函数取决于vptr所指向的虚函数表,编译器会在其中寻找相应的函数指针。

当你声明抽象基类而又没有相应的纯虚函数时,可以将析构函数声明为纯虚函数,但必须要为它提供定义

*请记住 : *

1. polymorphic(带多态性质的) base classes 应该声明一个 virtual析构函数。如果 class 带有任何 virtual函数(证明要充当某些类的基类),他就应该拥有一个 virtual析构函数。

2. Classes 的设计目的如果不是作为 base classes 使用,或不是为了其多态特性,就不应该声明virtual析构函数。

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

​ 当一个对象销毁时会自动调用析构函数,它负责释放此对象所占用的资源,若在析构函数中出现异常,且在析构函数内没有捕捉,那么该异常会向上传递,或许你在上层捕捉它并做出相应处理或许程序退出,但不要忘了此时你的析构函数还没有执行完,也就是说你的资源没有释放。

​ **解决方法 : **在析构函数中捕捉异常,确保析构函数能够顺利执行完。

*请记住 : *

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

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

​ 构造函数 : 当在基类构造函数中包含virtual函数,假设该类有派生类,由于在派生类构造函数调用时,会 先调用基类构造函数,此时派生类成员未初始化,那么基类中的virtual函数不会显示多态性, 不会调用相应的virtual函数。

​ 析构函数 : 同理,当派生类的的析构函数运行时,派生类的成员可能会被释放,所以C++干脆把他们视为不存 在,进入base class析构函数后,对象就变成一个基类对象,此时virtual没有意义。

​ 根本原因 : 派生类的基类构造函数构造析构期间,派生类对象被视为base class而不derive dclass

*请记住 : *

1. 在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(相对当前执行构造函数和析构函数的那一层)。

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

​ 这样的以进行连续赋值操作,就像内置类型 :

int z, y, z;
x = y = z = 1;

​ 此条同样适用其他像 +=、-=等与赋值相关的运算符。

请记住 :

1. 令赋值操作符返回一个reference to *this

条款11 在 operator= 中处理 “自我复制”

​ 在定义 operator=函数时,注意要先判同,防止自我赋值,自我复制有时会导致自己的数据被释放掉导致出错。

​ 也可以适用 copy and swap技术,起始就是先将参数复制一份副本,再适用标准库的swap()函数。

请记住 :

1. 确保当对象自我赋值时 operator= 有良好的行为。其中技术包括比较"来源对象"和"目标对象"的地址

、精心周到的语句顺序、以及 copy and swap。

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

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

​ 当编写一个copying函数时。请确保 :

​ 1. 复制所有 local 成员。

​ 2. 调用所有base class内的适当的 copying函数。

​ 注意,复制构造函数和复制操作符往往有有着相同的操作,但不应该为了去除冗余代码而相互调用

​ 理由:

​ 1. 用 copy assignment操作符调用 copy构造函数 : 试图构造一个已经存在的对象,逻辑上不太合理,可 能代码能实现相应的功能,但不推荐使用。

​ 2. 用 copy构造函数调用copy assignment操作符 : 同上分析。

​ 若想去除冗余代码,可以将冗余代码放进第三个函数中,供两者调用。

请记住 :

1. Copying 函数应该确保赋值“对象内的所有成员变量”及“所有base class成分。”

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

标签:Chapter,函数,virtual,析构,copy,class,构造函数
来源: https://www.cnblogs.com/Lingh/p/16618435.html