[効果的なc ++]構築と破壊

項目7.多形基本クラスの仮想デストラクタを宣言する
Car *c = new YellowCar();
delete c;

cはYellowCarオブジェクトを指しますが、cはCarポインタです。Carのデストラクタが仮想関数でない場合、削除時にCarのデストラクタのみが呼び出されます。YellowCarに動的に割り当てられたメモリがある場合、このピースのリリースロジックは通常、そのデストラクタで完了します。削除すると、YellowCarのデストラクタを呼び出すことができず、メモリリークが発生します。
ただし、ポリモーフィズムが使用されていない場合、つまり基本クラスに仮想関数がなく、基本クラスのデストラクタが仮想として宣言されている場合、このアプローチは適切ではないことに注意してください。まず、仮想関数は仮想関数テーブルポインターを介してアクセスする必要があり、仮想テーブルポインターはオブジェクトのスペースを占有します。第二に、型変換は不確実性をもたらします。したがって、余計なことをする必要はありません。

項目8、例外をデストラクタから逃がさないでください

逆に、doSomething関数の実行が終了すると、コンテナーvに格納されている各ウィジェットオブジェクトの分解が開始されます。ただし、オブジェクトデストラクタは例外を吐き出す可能性があります。この場合、コンテナ内の異常な数のオブジェクトが吐き出され、プログラムの動作に不確実性が生じます

class Widget {
    
    
public:
    //......
    ~Widget() {
    
        // 假定这个析构函数可能会吐出异常
        //......
    }
    //......
};

void doSomething()
{
    
    
    //......
    std::vector<Widget> v;
    //......
}

使用する正しい方法は、スローさせるのではなく、破棄で例外を処理します

// 修改后的DBConn类实现
class DBConn {
    
    
public:
    //......
    void close() {
    
        // 要求用户自己关闭数据库对象
        db.close();
        closed = true;
    }
    //......
    ~DBConn() {
    
    
        if (!closed) {
    
        // 如果用户忘记了这么做,就采用 try catch 机制吞下异常。
            try {
    
    
                db.close();
            }
            catch (...) {
    
    
                // 记录此次 close 失败
                //......
            }
        }
    }
    //......
private:
    //......
    DBConnection db;
    bool closed;    // 增设此变量用以判断用户是否已经自行调用 close(),用户也可根据此变量判断 close() 是否顺利执行并作出相应的异常处理。
    //......
};
項目9.コンストラクタおよびデストラクタで仮想関数を呼び出さないでください
class Transaction {
    
    // 所有交易的基类
public:
 Transaction();
 virtual void logTransaction() const = 0;//建立依赖于具体交易类型的登录项
 ...
};
Transaction::Transaction() //实现基类的构造函数
{
    
     
 ...
 logTransaction(); //最后,登录该交易
} 
class BuyTransaction: public Transaction {
    
     
// 派生类
public:
 virtual void logTransaction() const; //怎样实现这种类型交易的登录? 
 ...
};
class SellTransaction: public Transaction {
    
     
//派生类
public:
 virtual void logTransaction() const; //怎样实现这种类型交易的登录?
 ...
};

基本クラスの構築中、仮想関数呼び出しが派生クラスに渡されることはありません。代わりに、派生クラスオブジェクトは、それ自体が基本型であるかのように動作します。素人の言葉で言えば、仮想関数は基本クラスの構築中に「構築」されません。
基本クラスコンストラクターは派生クラスの前に実行されるため、基本クラスコンストラクターの実行時に派生クラスのデータメンバーは初期化されていません。基本クラスの構築中に仮想関数呼び出しが派生クラスに渡された場合、派生クラスオブジェクトはもちろんローカルデータメンバーを参照できますが、これらのデータメンバーはまだ初期化されていません。これにより、無限の未定義の動作と夜間のコードデバッグが発生します。
派生クラスのデストラクタが実行されると、オブジェクトの派生データメンバーは未定義の値であると見なされるため、C ++はそれらを存在しないものとして扱います。基本クラスのデストラクタに入ると、オブジェクトは基本クラスオブジェクトになり、C ++のすべての部分(仮想関数、dynamic_cast演算子など)がこのように処理されます。

おすすめ

転載: blog.csdn.net/niu91/article/details/112862868