4

以下のサンプル コードで、基底クラスのデストラクタが 2 回呼び出されるのはなぜですか?

class Base {
public:
    Base() {
        std::cout << "Base::Base()" << std::endl;
    }

    ~Base() {
        std::cout << "Base::~Base()" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        std::cout << "Derived::Derived()" << std::endl;
    }

    ~Derived() {
        std::cout << "Derived::~Derived()" << std::endl;
    }
};

int main() {
    Base a = Derived();
    return EXIT_SUCCESS;
}

プログラムを実行したときの出力のサンプルを次に示します。

Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
Base::~Base()
4

8 に答える 8

16

何が起こるかはスライスと呼ばれます。Basetype のオブジェクトを type のオブジェクトで初期化しますDerived。型Derivedのオブジェクトには型のオブジェクトもBase含まれているため (「基本クラス サブオブジェクト」と呼ばれます)、プログラム全体で2 つのBaseオブジェクトと 1 つのオブジェクトが存在します。DerivedDerived オブジェクト (およびそのタイプ の基本クラス サブオブジェクトBase) は初期化時にのみ存在し、残りのBaseオブジェクトは の終わりまで存在しますmain

2 つの Base オブジェクトと 1 つの Derived オブジェクトがあるため、もう 1 つの Base デストラクタも実行されます。

于 2009-05-21T18:04:33.790 に答える
7

コピー コンストラクターが使用されています。何が起こっているかを確認したい場合は、コピー コンストラクターもインストルメント化します。

 Base( const Base & ) {
        std::cout << "Base::Base( const Base &)" << std::endl;
    }

Derived についても同様です。

これは、デストラクタが仮想でないこととは何の関係もないことに注意してください。

于 2009-05-21T18:04:21.870 に答える
4

Derived()inと言うと、main()オブジェクト a にコピーされる一時オブジェクトが作成されます。したがって、デストラクタが2回呼び出されるため、2つのオブジェクトがあります。また、他の人が指摘したように、基本クラスのデストラクタは仮想でなければなりません。

于 2009-05-21T18:00:22.753 に答える
2

Derivedコピー構築する前に一時的なタイプを作成するためですa。したがって、これは基本的に何が起こるかです:

Derived d(); // Your temporary of type Derived is created
Base a(d); // The temporary is used to call a's copy constructor 
d.Derived::~Derived(); // The temporary is destroyed, calling both ~Derived and ~Base
a.Base::~Base(); // The nonvirtual destructor on a is called, so ~Base is called, but not ~Derived

したがって、最初の不要なコピー (コンパイラが最適化して削除する可能性があります) を除けば、実際のエラーは ~Base が仮想ではないということです。

編集 おっと、litbが指摘したように行われるスライスを完全に見逃しました。代わりに彼の答えを読んでください:)

于 2009-05-21T18:06:17.017 に答える
1

以下を追加すると、プログラムがより明確になります。

 Base(const Base& base){
        std::cout << "Base::Base(const Base& base)" << std::endl;
 }

コンパイラは自動的にコピー コンストラクターを作成します。それを自分で定義することで(そして出力に追加することで)、コンストラクタとデストラクタの数が一致することがわかります

Base::Base()
Derived::Derived()
Base::Base(const Base& base)
Derived::~Derived()
Base::~Base()
Base::~Base()
于 2009-05-21T18:14:46.607 に答える
0

1) Derived 型の一時オブジェクトが構築されます (Derived::Derived() および Base::Base() が呼び出されます)。

2) 一時オブジェクトが「a」にコピーされます

3) 一時オブジェクトが破棄されます ( Derived::~Derived() および Base::~Base() が呼び出されます)

4) EXIT_SUCCESS を返します。

5) "a" が破棄されるため、Base::~Base() が呼び出されます

于 2009-05-21T23:06:33.633 に答える
0

仮想デストラクタが必要です。

于 2009-05-21T17:59:23.660 に答える
0

1 つのスタック変数と 1 つの一時 (合計 2 つのオブジェクトが構築されている) があるため、デストラクタが 2 回呼び出されるのは論理的です。

于 2009-05-21T18:07:36.590 に答える