Apakah saya perlu secara eksplisit memanggil basis destructor virtual?

350

Ketika mengganti kelas di C ++ (dengan destruktor virtual) saya mengimplementasikan destruktor lagi sebagai virtual pada kelas pewarisan, tetapi apakah saya perlu memanggil destruktor basis?

Kalau begitu saya bayangkan itu seperti ini ...

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

    // Some destructing specific to MyChildClass
}

Apakah saya benar?

Nick Bolton
sumber

Jawaban:

469

Tidak, destruktor dipanggil secara otomatis dalam urutan terbalik konstruksi. (Kelas dasar terakhir). Jangan panggil destruktor kelas dasar.

Lou Franco
sumber
Bagaimana dengan destruktor virtual murni? Linker saya mencoba menyebutnya di akhir destruktor non-virtual kelas bawaan saya;
cjcurrie
40
Anda tidak dapat memiliki destruktor virtual murni tanpa badan. Berikan saja tubuh kosong. Dengan metode virtual murni biasa, fungsi utama dipanggil sebagai gantinya, dengan destruktor, semuanya dipanggil, jadi Anda harus menyediakan body. Nilai = 0 hanya berarti harus diganti, jadi tetaplah konstruksi yang berguna jika Anda membutuhkannya.
Lou Franco
1
Pertanyaan ini mungkin terkait dan membantu pertanyaan / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole
Mengapa kode Nick Bolton tidak menyebabkan kesalahan segmentasi meskipun memanggil destruktor basis dua kali, sementara memanggil deletepointer ke kelas dasar dua kali tidak menyebabkan kesalahan segmentasi?
Maggyero
2
Anda tidak dijamin kesalahan segmentasi dengan kode yang salah. Juga, memanggil destruktor tidak melepaskan memori.
Lou Franco
92

Tidak, Anda tidak perlu memanggil destruktor basis, destruktor basis selalu dipanggil untuk Anda oleh destruktor yang diturunkan. Silakan lihat jawaban terkait saya di sini untuk urutan kehancuran .

Untuk memahami mengapa Anda menginginkan destruktor virtual di kelas dasar, silakan lihat kode di bawah ini:

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


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

Saat kamu melakukan:

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

Maka jika Anda tidak memiliki destruktor virtual di B, hanya ~ B () yang akan dipanggil. Tetapi karena Anda memiliki destruktor virtual, pertama ~ D () akan dipanggil, kemudian ~ B ().

Brian R. Bondy
sumber
20
Harap sertakan keluaran program (semu). ini akan membantu pembaca.
Kuldeep Singh Dhaka
@KuldeepSinghDhaka Pembaca dapat melihatnya langsung di wandbox.org/permlink/KQtbZG1hjVgceSlO .
babi
27

Apa yang orang lain katakan, tetapi juga perhatikan bahwa Anda tidak harus mendeklarasikan virtual destructor di kelas turunan. Setelah Anda mendeklarasikan virtual destruktor, seperti yang Anda lakukan di kelas dasar, semua destruktor yang diturunkan akan menjadi virtual apakah Anda menyatakan demikian atau tidak. Dengan kata lain:

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

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

struct C : public A {
   ~C() {}          // this is virtual too
};
Wodzu
sumber
1
bagaimana jika ~ B tidak dinyatakan virtual? Apakah ~ C masih virtual?
Will
5
Iya. Ketika metode virtual (apa pun, bukan hanya destruktor) dinyatakan virtual, semua penggantian metode itu di kelas turunan secara otomatis virtual. Dalam hal ini, bahkan jika Anda tidak mendeklarasikan ~ B virtual, itu masih ada, dan begitu juga ~ C.
boycy
1
Tetapi tidak seperti metode yang ditimpa lainnya yang memiliki nama dan parameter yang sama dari metode yang sesuai di kelas dasar, nama destruktor berbeda. Akankah penting? @boycy
Yuan Wen
1
@YuanWen tidak, tidak, destruktor (satu-satunya) yang diturunkan selalu mengesampingkan (satu-satunya) destruktor kelas dasarnya.
boycy
10

Tidak seperti metode virtual lainnya, di mana Anda akan secara eksplisit memanggil metode Basis dari Turun ke 'rantai' panggilan, kompiler menghasilkan kode untuk memanggil destruktor dalam urutan terbalik di mana konstruktor mereka dipanggil.

itsmatt
sumber
9

Tidak, Anda tidak pernah memanggil destruktor kelas dasar, selalu dipanggil secara otomatis seperti yang ditunjukkan orang lain, tetapi di sini ada bukti konsep dengan hasil:

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;
}

Outputnya adalah:

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 . . .

Jika Anda menetapkan destruktor kelas dasar sebagai virtual mana yang harus, maka hasil huruf 3 akan sama dengan kasus 1 & 2.

zar
sumber
Ilustrasi yang bagus. Jika Anda mencoba memanggil destruktor kelas dasar dari kelas turunan, Anda akan mendapatkan kesalahan kompiler yang mirip dengan "kesalahan: tidak ada fungsi yang cocok untuk panggilan ke 'BASE :: BASE ()' <newline> ~ BASE ();" Setidaknya ini adalah perilaku dari kompiler g ++ 7.x saya.
Kemin Zhou
6

Tidak. Secara otomatis dipanggil.

Benoît
sumber
1

Destructors di C ++ secara otomatis dipanggil sesuai urutan konstruksinya (Berasal dari Base) hanya ketika destructor kelas Base dideklarasikanvirtual .

Jika tidak, maka hanya destruktor kelas dasar yang dipanggil pada saat penghapusan objek.

Contoh: Tanpa Destructor 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;

}

Keluaran

Base Constructor
Derived Constructor
Base Destructor

Contoh: Dengan Basis Destructor virtual

#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;

}

Keluaran

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Direkomendasikan untuk mendeklarasikan destruktor kelas dasar karena virtualjika tidak, akan menyebabkan perilaku yang tidak terdefinisi.

Referensi: Destructor Virtual

Adarsh ​​Kumar
sumber