ベース仮想デストラクタを明示的に呼び出す必要がありますか?


350

C ++でクラスをオーバーライドする場合(仮想デストラクタを使用)、継承するクラスで仮想としてデストラクタを再度実装しますが、基本デストラクタを呼び出す必要がありますか?

もしそうなら、それはこのようなものだと思います...

MyChildClass::~MyChildClass() // virtual in header
{
    // Call to base destructor...
    this->MyBaseClass::~MyBaseClass();

    // Some destructing specific to MyChildClass
}

私は正しいですか?

回答:


469

いいえ、デストラクタは構築の逆の順序で自動的に呼び出されます。(基本クラスは最後です)。基本クラスのデストラクターを呼び出さないでください。


純粋な仮想デストラクタはどうですか?私のリンカは、継承したクラスの非仮想デストラクタの最後でそれを呼び出そうとしています。
cjcurrie 2013

40
ボディなしで純粋な仮想デストラクタを作成することはできません。空のボディを与えるだけです。通常の純粋仮想メソッドでは、代わりにオーバーライド関数が呼び出され、デストラクタですべて呼び出されるため、本体を提供する必要があります。= 0は、オーバーライドする必要があることを意味するだけなので、必要な場合でも便利な構成です。
ルーフランコ

1
この質問は関連していて、questions / 15265106 / ca-missing-vtable-errorを助けるかもしれません。
Paul-Sebastian Manole 2014

ニックボルトンのコードはベースデストラクタを2回呼び出しdeleteますが、ベースクラスへのポインタを2回呼び出すとセグメンテーションエラーが発生するのに、なぜセグメンテーションエラーは発生しませんか?
マギェロ

2
間違ったコードによるセグメンテーション違反は保証されません。また、デストラクタを呼び出してもメモリは解放されません。
ルーフランコ

92

基本デストラクタを呼び出す必要はありません。基本デストラクタは常に派生デストラクタによって呼び出されます。 破壊の順序については、こちらの関連​​する回答をご覧ください

基本クラスに仮想デストラクタが必要な理由を理解するには、以下のコードを参照してください。

class B
{
public:
    virtual ~B()
    {
        cout<<"B destructor"<<endl;
    }
};


class D : public B
{
public:
    virtual ~D()
    {
        cout<<"D destructor"<<endl;
    }
};

あなたがするとき:

B *pD = new D();
delete pD;

次に、Bに仮想デストラクタがない場合は、〜B()のみが呼び出されます。ただし、仮想デストラクタがあるため、最初に〜D()が呼び出され、次に〜B()が呼び出されます。


20
プログラム(疑似)出力を含めてください。それは読者を助けるでしょう。
クルディープシンダッカ2016

@KuldeepSinghDhaka読者はそれがに住んで見ることができますwandbox.org/permlink/KQtbZG1hjVgceSlO

27

他の人が言ったことだけでなく、派生クラスで仮想デストラクターを宣言する必要がないことにも注意してください。基本クラスの場合と同様に、デストラクタを仮想として宣言すると、宣言したかどうかに関係なく、すべての派生デストラクタは仮想になります。言い換えると:

struct A {
   virtual ~A() {}
};

struct B : public A {
   virtual ~B() {}   // this is virtual
};

struct C : public A {
   ~C() {}          // this is virtual too
};

1
〜Bが仮想として宣言されていない場合はどうなりますか?〜Cはまだ仮想ですか?
ウィル

5
はい。仮想メソッド(デストラクタだけでなく任意のもの)が仮想として宣言されると、派生クラス内のそのメソッドのオーバーライドはすべて自動的に仮想になります。この場合、〜B virtualを宣言しなくても、〜Cはそうです。
ボイシー

1
しかし、基本クラス内の対応するメソッドの同じ名前とパラメーターを持つ他のオーバーライドされたメソッドとは異なり、デストラクター名は異なります。@boycy
Wen

1
@YuanWenいいえ、そうではありません。(唯一の)派生したデストラクタは、常にその基本クラスの(唯一の)デストラクタをオーバーライドします。
ボイシー

10

いいえ。他の仮想メソッドとは異なり、Derivedから明示的にBaseメソッドを呼び出して呼び出しを「チェーン」する場合、コンパイラーは、コンストラクターが呼び出された逆の順序でデストラクタを呼び出すコードを生成します。


9

いいえ、基本クラスのデストラクターを呼び出すことはありません。他の人が指摘したように、常に自動的に呼び出されますが、ここに結果の概念実証があります。

class base {
public:
    base()  { cout << __FUNCTION__ << endl; }
    ~base() { cout << __FUNCTION__ << endl; }
};

class derived : public base {
public:
    derived() { cout << __FUNCTION__ << endl; }
    ~derived() { cout << __FUNCTION__ << endl; } // adding call to base::~base() here results in double call to base destructor
};


int main()
{
    cout << "case 1, declared as local variable on stack" << endl << endl;
    {
        derived d1;
    }

    cout << endl << endl;

    cout << "case 2, created using new, assigned to derive class" << endl << endl;
    derived * d2 = new derived;
    delete d2;

    cout << endl << endl;

    cout << "case 3, created with new, assigned to base class" << endl << endl;
    base * d3 = new derived;
    delete d3;

    cout << endl;

    return 0;
}

出力は次のとおりです。

case 1, declared as local variable on stack

base::base
derived::derived
derived::~derived
base::~base


case 2, created using new, assigned to derive class

base::base
derived::derived
derived::~derived
base::~base


case 3, created with new, assigned to base class

base::base
derived::derived
base::~base

Press any key to continue . . .

基本クラスのデストラクタを仮想として設定すると、ケース3の結果はケース1および2と同じになります。


良いイラスト。派生クラスから基本クラスデストラクタを呼び出そうとすると、「エラー: 'BASE :: BASE()'への呼び出しに一致する関数がありません<newline>〜BASE();」のようなコンパイラエラーが発生します。少なくとも、これは私のg ++​​ 7.xコンパイラの動作です。
Kemin Zhou、


1

C ++のデストラクタは、Baseクラスのデストラクタが宣言されている場合にのみ、その構造(Derived then Base)の順序で自動的に呼び出されます。virtual

そうでない場合は、オブジェクトの削除時に基本クラスのデストラクタのみが呼び出されます。

例:仮想デストラクタなし

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

出力

Base Constructor
Derived Constructor
Base Destructor

例:ベース仮想デストラクタを使用

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  //virtual destructor
  virtual ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
    delete(n);  //deleting the memory used by pointer
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

出力

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

それvirtual以外の場合は、基本クラスのデストラクターを宣言することをお勧めします。そうしないと、未定義の動作が発生します。

リファレンス:Virtual Destructor

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.