【Effective C++ 条款08-12 笔记】【别让异常逃离析构函数】【不在构造和析构过程中调用virtual函数】【令operator=返回一个绑定到*this的引用】...

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

  • C++并不禁止析构函数抛出异常,但是不建议这样做

  • 有两个异常存在的情况下,程序不是结束执行就是导致不明确的行为

如果无法避免析构函数产生异常的可能性,有两种办法:

1、若析构函数爬出异常,就调用abort结束程序

A::~A() {
    
    
    try {
    
    a.func();}
    catch(...) {
    
    
        //记录调用a.func()过程中出现异常
        abort();
    }
}

2、吞下异常,当做什么也没发生过

A::~A() {
    
    
    try {
    
    a.func();}
    catch(...) {
    
    
        //记录调用a.func()过程中出现异常
    }
}

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

子类对象调用子类构造函数之前,会先调用父类构造函数。若子类重写了父类中的一个虚函数func(),父类的构造函数中调用了func()函数,那么当子类对象调用父类的构造函数执行到这一句时,会调用父类版本的func()函数。

这是因为:

  • 父类的构造函数的执行早于子类的构造函数,当父类的构造函数执行时,子类的成员变量尚未初始化,如果此时调用的虚函数下降至子类层次,会存在使用子类未初始化成员的风险,因此C++禁止这种危险。

更根本的原因是:

  • 子类对象在调用父类构造函数期间,对象类型是基类而不是子类,不仅虚函数会使用父类版本,此时使用dynamic_cast和typeid,也会把对象视为基类类型

析构函数也是一样的原因

条款10:令operator=返回一个绑定到*this的引用

形式:

class A {
    
    
public:
    ...
    A& operator=(const A& other) {
    
    
        ...
        return *this;
    }
}

这是为了实现连锁赋值,即:

int x, y, z;
x = y = z = 100;
//运行原理
x = (y = (z = 100))

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

自我赋值的意思就是:

等号右边的东西和等号左边的东西是同一份(地址相同)

a[i] = a[j];// i == j成立

一般的做法是在赋值前做一个检查:

A& A::operator=(const A& other) {
    
    
    if (this == &other)	// 比较双方地址
        return *this;
    delete pb;
    pb = new B(*other.pb);
    return *this;
}

条款12:赋值对象时勿忘其每一个成分

复制函数包括:拷贝构造函数、拷贝赋值运算符

当自定义了复制函数后,编译器不会再生成补充版本(即便自定义的有问题)

  • 当为class添加一个成员变量,必须同时修改复制函数
  • 为子类自定义复制函数时,要注意对基类成员的复制。基类成员通常是private,子类无法直接访问,应该让子类的复制函数调用相应的基类复制函数

不能为了简化代码,就在拷贝构造函数中调用拷贝赋值运算符,也不能在拷贝赋值运算符中调用拷贝构造函数。因为构造函数用来初始化新对象,而赋值运算符只能用于已初始化的对象之上。

要想消除复制函数的重复代码,可以建立一个新的成员函数给复制函数调用,这个函数通常是private且命名为init

Guess you like

Origin blog.csdn.net/weixin_44484715/article/details/121387887