web-dev-qa-db-ja.com

継承を使用してC ++でデストラクタが呼び出され、メンバー変数が破棄される順序は何ですか?

正確ではないことを除いて、これらと非常によく似た質問: C++でデストラクタとコンストラクタが呼び出される順序は何ですかメンバーコンストラクタとデストラクタが呼び出される順序

知りたいのですが、派生クラスのメンバー変数は、基本クラスのデストラクタが呼び出される前または後に破棄されますか?

これは、Visual Studio2008を使用するC++です。ありがとうございます。

10
mczarnek

コンストラクター:最初のベース、次に派生

破壊:

  • 〜派生
  • 〜メンバー派生
  • 〜base
  • 〜メンバーベース

コード:

class member {
    string s;

public:
    member(string s) {
        this-> s = s;
    }

    ~member() {
        cout << "~member " << s << endl;
    }
};

class base {
    member m;
public:
    base() : m("base"){
    }

    ~base() {
        cout << "~base" << endl;
    }
};

class derived : base{
     member m2;
public:

    derived() :m2("derived") {    }

    ~derived() {
        cout << "~derived" << endl;
    }
};

int main(int argc, const char * argv[]) {
    derived s;

    return 0;
}

参照と仮想デストラクタ

派生オブジェクトを動的に割り当てる場合(つまり、キーワードnewdeleteを使用する場合)、常にvirtualまたはprotectedベースのデストラクタ。基本クラス参照のオブジェクトを動的に削除すると、以下の例ではメモリリークになります。

class base {
    member m;
public:
    base() : m("base"){
    }

    /* correct behaviour is when you add **virtual** in front of the signature */
    ~base() {
        cout << "~base" << endl;
    }
};

class derived : public base{
     member m2;
    char* longArray;
public:

    derived() :m2("derived") {
        longArray = new char[1000];
    }


    ~derived() {
        delete[] longArray; // never called
        cout << "~derived" << endl;
    }
};

int main(int argc, const char * argv[]) {
    base *s = new derived; // mind the downcast to **base**

    delete s; /* only the non-virtual destructor on the base and its members is called. 
               No destructor on derived or its members is called.
               What happens to the memory allocated by derived?
               **longArray** is leaked forever. 
               Even without **longArray**, it most probably **leaks** memory, as C++ doesn't define its behaviour 
               */
    return 0;
}

出力:

  • 〜base
  • 〜メンバーベース

基本データのみがクリーンアップされ、longArrayleaks

15
tofi9

これが規格の内容です...(C++ 11、12.4/8)

デストラクタの本体を実行し、本体内に割り当てられた自動オブジェクトを破棄した後、クラスXのデストラクタは、Xの直接の非バリアント非静的データメンバーのデストラクタを呼び出します。 Xの直接基本クラス、およびXが最も派生したクラス(12.6.2)のタイプである場合、そのデストラクタはXの仮想基本クラスのデストラクタを呼び出します。すべてのデストラクタは、修飾名で参照されているかのように呼び出されます。つまり、より派生したクラスで可能な仮想オーバーライドデストラクタは無視されます。ベースとメンバーは、コンストラクターの完了とは逆の順序で破棄されます(12.6.2を参照)。デストラクタのreturnステートメント(6.6.3)は、呼び出し元に直接返されない場合があります。呼び出し元に制御を移す前に、メンバーとベースのデストラクタが呼び出されます。配列の要素のデストラクタは、その構成の逆の順序で呼び出されます(12.6を参照)。

この順序は、C++ 11の12.6.2/10で指定された順序の逆であることに注意してください。 12.4/8だけを見ても、仮想ベースの破壊の順序はわかりませんが、深さ優先探索で仮想ベースの初期化が行われることを指定する12.6.2/10から推測できます。 -正しい順序。 (したがって、仮想ベースの破壊は、その順序の逆で発生します。)

とにかく、あなたはあなたの答えを持っています。非静的メンバーが最初に破棄され、次に基本クラスが破棄されます。ただし、基本クラスのメンバーは、next基本クラスのデストラクタが起動する前に破棄されます。深さ優先探索とまったく同じです。

6
Brian