c++解析関数、虚解析関数、純虚解析関数の詳細

3768 ワード

オブジェクトがライフサイクルを終了する前に、構造関数を呼び出して必要なクリーンアップ作業を完了することを知っています.派生クラス呼び出しの構造関数の順序は「先サブクラス、後ベースクラス」である.この文章は、構造関数が通常の構造関数、虚構造関数、純虚構造関数である場合、delete演算子を使用してポインタオブジェクトを削除すると、構造関数に何が起こるかをまとめるために使用します.
####一般解析関数####
CBaseはベースクラスであり、CDeriveはそのサブクラスであり、クラスソースコードは以下の通りである.
class CBase
{
public:
    CBase(){}
    //      
    ~CBase(){ cout << "CBase Destructor" << endl; } 
private:
    int a;
};

class CDerive:public CBase
{
public:
    CDerive(){}   
    //      
    ~CDerive(){ cout << "CDerive Destructor" << endl; }
private:
    int b;
};

テストコードは次のとおりです.
//case1
CDerive *pDeriveObj = new CDerive();
delete pDeriveObj;

//case2
//             ,              (           ),
//  pBaseObj        ,         
CBase* pBaseObj = new CDerive();
delete pBaseObj; //         

テスト結果:
/* case 1
     :CDerive Destructor
     :CBase Destructor
*/

/* case2
     :CBase Destructor
*/

まとめ:
  • delete演算子がサブクラスポインタオブジェクトを削除すると、サブクラスとベースクラスの構造関数が呼び出されます.
  • 解析関数が非虚である場合、ベースクラスポインタがサブクラスオブジェクトを指している場合でもdeleteポインタオブジェクトの場合、ベースクラスの解析関数のみが呼び出されます.

  • ##############################################################
    ベースクラスの構造関数が虚関数に設定されている場合、deleteベースクラスのポインタオブジェクトでは、実際のタイプに基づいてオブジェクトのクリーンアップを完了できます.ソースコードは次のとおりです.
    class CBase
    {
    public:
        CBase(){}
        //       
        virtual ~CBase(){ cout << "CBase Destructor" << endl; } 
    private:
        int a;
    };
    
    class CDerive:public CBase
    {
    public:
        CDerive(){}   
        //       
        virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
    private:
        int b;
    };
    

    テストコード:
     //      
     CBase*pBaseObj_case1 = new CBase();
     delete pBaseObj_case1;
     
     //      
     CBase*pBaseObj_case2 = new CDerive();
     delete pBaseObj_case2;
    

    実行結果://case 1 CBase Destructor
    //case 2 CDerive Destructor->先サブクラスCBase Destructor->後ベースクラス
    まとめ:ベースクラスの解析関数が虚関数である場合、ベースクラスポインタがサブクラスオブジェクトを指す場合、delete演算子を使用してポインタオブジェクトを削除し、解析関数は「先サブクラス、後ベースクラス」の原則に従ってオブジェクトのクリーンアップを完了することができる.このように多重継承されたクラスでは、各クラスが正しく整理されることを保証することができる.例えば、ベースクラスとサブクラスのバッファは解放されます.
    ###純虚析構造関数####
    ベースクラスに純粋な虚関数がある場合、ベースクラスはインスタンス化されず、サブクラスに純粋な虚関数を書き換える必要がある.純粋な虚構造関数には、ソースコードが以下のように特殊です.
    class CBase
    {
    public:
        CBase(){}
        //      
        virtual ~CBase()= 0;
    private:
        int a;
    };
    
    class CDerive:public CBase
    {
    public:
        CDerive(){}   
        //      
        virtual ~CDerive(){ cout << "CDerive Destructor" << endl; }
    private:
        int b;
    };
    

    テストコード:
    CBase*pBaseObj = new CDerive();
    delete pBaseObj;
    

    コードをコンパイルすると、コードコンパイルがヒントにならないことがわかります.「error LNK 209:解析できない外部シンボル「public:virtual__thiscallCBase:~CBase(void)」(?1 CBase@@UAE@XZ)で、この記号は関数"public:virtual__thiscallCDerive:~CDerive(void)"(?1 CDerive@@UAE@XZ)で参照される理由は、サブクラスの解析時にベースクラスの解析関数を呼び出す必要があるためであるが、コードにCBase解析関数が実装されていないことが判明し、コンパイル異常を招いた.解決策は,サブクラスで書き換えるのではなくCBaseクラス外でその関数体を実現することであり,~CDerive関数も虚関数であり,その関数名が異なるとしても.これは他の純虚関数と特別な場所がある.
    CBaseクラスの外に次のような構造関数を追加する必要があります.
    CBase::~CBase()
    { 
        cout << "CBase Destructor" << endl; 
    } 
    

    これにより、CDerive Destructor->先サブクラスCBase Destructor->後ベースクラス
    ###まとめ###
    ベースクラスの構造関数を虚関数として宣言したほうがいい.これにより、すべての派生クラスの構造関数が自動的に虚関数になります.このように、プログラムでdelete演算子を明示的に使用してオブジェクトを削除しようとし、delete演算子の操作オブジェクトが派生クラスオブジェクトを指すベースクラスポインタを使用すると、対応するクラスの構造関数が呼び出されます.
    プロフェッショナルは一般的に、仮想構造関数を宣言することに慣れています.ベースクラスが構造関数を必要としなくても、動的割り当て空間を取り消すときに正しい処理が得られるように、関数体が空の仮想構造関数を明示的に定義します.
    参考資料:
    http://c.biancheng.net/cpp/biancheng/view/247.html
    http://blog.csdn.net/yapian8/article/details/46418687