Pertanyaan Kapan menggunakan virtual destructors?


Saya memiliki pemahaman yang kuat tentang kebanyakan teori OO tetapi satu hal yang membingungkan saya banyak adalah virtual destructors.

Saya berpikir bahwa destructor selalu dipanggil tidak peduli apa dan untuk setiap objek dalam rantai.

Kapan Anda bermaksud menjadikannya virtual dan mengapa?


1206
2018-01-20 12:58


asal


Jawaban:


Virtual destructors berguna ketika Anda dapat menghapus turunan kelas turunan melalui pointer ke kelas dasar:

class Base 
{
    // some virtual methods
};

class Derived : public Base
{
    ~Derived()
    {
        // Do some important cleanup
    }
};

Di sini, Anda akan melihat bahwa saya tidak menyatakan destruktor Base virtual. Sekarang, mari kita lihat cuplikan berikut:

Base *b = new Derived();
// use b
delete b; // Here's the problem!

Karena destruktor Base tidak virtual dan b adalah Base* menunjuk ke a Derived obyek, delete b punya perilaku tidak terdefinisi:

[Di delete b], jika tipe statis dari   objek yang akan dihapus berbeda dari jenis dinamisnya, yang statis   jenis adalah kelas dasar dari tipe dinamis dari objek yang akan   dihapus dan tipe statis harus memiliki destruktor virtual atau   perilaku tidak terdefinisi.

Dalam sebagian besar implementasi, panggilan ke destructor akan diselesaikan seperti kode non-virtual, yang berarti bahwa destruktor kelas dasar akan dipanggil tetapi bukan kelas turunan, yang mengakibatkan kebocoran sumber daya.

Singkatnya, selalu buat destruktor kelas dasar virtual ketika mereka dimaksudkan untuk dimanipulasi secara polimorfik.

Jika Anda ingin mencegah penghapusan instance melalui pointer kelas dasar, Anda dapat membuat destruktor kelas dasar dilindungi dan nonvirtual; dengan demikian, kompilator tidak akan membiarkan Anda menelepon delete pada pointer kelas dasar.

Anda dapat mempelajari lebih lanjut tentang virtualitas dan destruktor kelas dasar virtual di artikel ini dari Herb Sutter.


1310
2018-01-20 13:04



Deklarasikan destruktor virtual dalam kelas dasar polimorfik. Ini adalah Item 7 di Scott Meyers ' Efektif C ++. Meyers melanjutkan dengan meringkas bahwa jika kelas memiliki apa saja fungsi virtual, harus memiliki destruktor virtual, dan kelas yang tidak dirancang untuk menjadi kelas dasar atau tidak dirancang untuk digunakan secara polimorfik harus tidak deklarasikan virtual destructors.


165
2018-01-20 13:11



Konstruktor virtual tidak mungkin tetapi destruktor virtual dimungkinkan. Mari kita bereksperimen ....

#include <iostream>

using namespace std;

class Base
{
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

Kode di atas menghasilkan yang berikut:

Base Constructor Called
Derived constructor called
Base Destructor called

Konstruksi objek turunan mengikuti aturan konstruksi tetapi ketika kita menghapus pointer "b" (pointer dasar), kita telah menemukan bahwa hanya destruktor dasar yang dipanggil. Tetapi ini tidak boleh terjadi. Untuk melakukan hal yang tepat kita harus membuat basis destructor virtual. Sekarang mari kita lihat apa yang terjadi berikut ini:

#include <iostream>

using namespace std;

class Base
{ 
public:
    Base(){
        cout << "Base Constructor Called\n";
    }
    virtual ~Base(){
        cout << "Base Destructor called\n";
    }
};

class Derived1: public Base
{
public:
    Derived1(){
        cout << "Derived constructor called\n";
    }
    ~Derived1(){
        cout << "Derived destructor called\n";
    }
};

int main()
{
    Base *b = new Derived1();
    delete b;
}

Output berubah sebagai berikut:

Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called

Jadi penghancuran pointer dasar (yang mengambil alokasi pada objek yang diturunkan!) Mengikuti aturan kehancuran yaitu pertama yang diturunkan dari basis. Di sisi lain untuk konstruktor tidak ada yang seperti konstruktor virtual.


153
2018-04-09 13:39



Juga perhatikan bahwa menghapus pointer kelas dasar ketika tidak ada destruktor virtual akan menghasilkan perilaku tidak terdefinisi. Sesuatu yang saya pelajari baru-baru ini:

Bagaimana seharusnya meng-override delete dalam C ++ berperilaku?

Saya telah menggunakan C ++ selama bertahun-tahun dan saya masih bisa menggantung diri.


37
2018-01-21 01:09



Buatlah destructor virtual kapan pun kelas Anda bersifat polimorfik.


30
2018-01-20 13:01



Memanggil destructor melalui pointer ke kelas dasar

struct Base {
  virtual void f() {}
  virtual ~Base() {}
};

struct Derived : Base {
  void f() override {}
  ~Derived() override {}
};

Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived

Panggilan destruktor virtual tidak berbeda dari panggilan fungsi virtual lainnya.

Untuk base->f(), panggilan akan dikirim ke Derived::f(), dan itu sama base->~Base() - fungsi utamanya - Derived::~Derived() akan dipanggil.

Sama terjadi ketika destructor dipanggil secara tidak langsung, mis. delete base;. Itu delete pernyataan akan memanggil base->~Base() yang akan dikirim ke Derived::~Derived().

Kelas abstrak dengan destruktor non-virtual

Jika Anda tidak akan menghapus objek melalui pointer ke kelas dasarnya - maka tidak perlu memiliki destruktor virtual. Buat saja protected sehingga tidak akan dipanggil secara tidak sengaja:

// library.hpp

struct Base {
  virtual void f() = 0;

protected:
  ~Base() = default;
};

void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.

//-------------------
// application.cpp

struct Derived : Base {
  void f() override { ... }
};

int main() {
  Derived derived;
  CallsF(derived);
  // No need for virtual destructor here as well.
}

10
2018-05-18 13:38



Saya suka berpikir tentang antarmuka dan implementasi antarmuka. Dalam antarmuka C ++ berbicara adalah kelas virtual murni. Destructor adalah bagian dari antarmuka dan diharapkan untuk diimplementasikan. Oleh karena itu destructor harus murni virtual. Bagaimana dengan konstruktor? Konstruktor sebenarnya bukan bagian dari antarmuka karena objek selalu dipakai secara eksplisit.


7
2017-11-08 16:28



Agar sederhana, Virtual destructor adalah untuk menghancurkan sumber daya dalam urutan yang tepat, ketika Anda menghapus pointer kelas dasar yang menunjuk ke objek kelas yang diturunkan.

 #include<iostream>
 using namespace std;
 class B{
    public:
       B(){
          cout<<"B()\n";
       }
       virtual ~B(){ 
          cout<<"~B()\n";
       }
 };
 class D: public B{
    public:
       D(){
          cout<<"D()\n";
       }
       ~D(){
          cout<<"~D()\n";
       }
 };
 int main(){
    B *b = new D();
    delete b;
    return 0;
 }

OUTPUT:
B()
D()
~D()
~B()

==============
If you don't give ~B()  as virtual. then output would be 
B()
D()
~B()
where destruction of ~D() is not done which leads to leak


6
2017-08-26 05:33



Kata kunci virtual untuk destructor diperlukan ketika Anda ingin destruktor yang berbeda harus mengikuti urutan yang tepat saat objek sedang dihapus melalui pointer kelas dasar. sebagai contoh:

Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ; 

Jika destruktor kelas turunan Anda adalah virtual maka objek akan dihancurkan secara berurutan (pertama-tama objek turunan lalu basis). Jika destruktor kelas turunan Anda TIDAK virtual maka hanya objek kelas dasar yang akan dihapus (karena pointer adalah kelas dasar "Base * myObj"). Jadi akan ada kebocoran memori untuk objek yang diturunkan.


5
2018-01-29 07:11



Perusak kelas dasar virtual adalah "praktik terbaik" - Anda harus selalu menggunakannya untuk menghindari kebocoran memori yang sulit terdeteksi. Menggunakannya, Anda dapat yakin semua destruktor dalam rantai warisan kelas Anda disebut beeing (dalam urutan yang benar). Mewarisi dari kelas dasar menggunakan destruktor virtual membuat destruktor kelas yang mewarisi otomatis virtual juga (jadi Anda tidak perlu mengetik ulang 'virtual' dalam deklarasi kelas destruktor yang mewarisi).


2
2018-01-24 16:22



Apa itu perusak virtual atau bagaimana menggunakan perusak virtual

Kelas destruktor adalah fungsi dengan nama yang sama dari kelas sebelumnya dengan ~ yang akan mengalokasikan kembali memori yang dialokasikan oleh kelas. Mengapa kita membutuhkan destruktor virtual

Lihat contoh berikut dengan beberapa fungsi virtual

Sampel juga memberi tahu bagaimana Anda dapat mengonversi huruf ke atas atau bawah

#include "stdafx.h"
#include<iostream>
using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
  //void convertch(){};
  virtual char* convertChar() = 0;
  ~convertch(){};
};

class MakeLower :public convertch
{
public:
  MakeLower(char *passLetter)
  {
    tolower = true;
    Letter = new char[30];
    strcpy(Letter, passLetter);
  }

  virtual ~MakeLower()
  {
    cout<< "called ~MakeLower()"<<"\n";
    delete[] Letter;
  }

  char* convertChar()
  {
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] + 32;
    return Letter;
  }

private:
  char *Letter;
  bool tolower;
};

class MakeUpper : public convertch
{
public:
  MakeUpper(char *passLetter)
  {
    Letter = new char[30];
    toupper = true;
    strcpy(Letter, passLetter);
  }

  char* convertChar()
  {   
    size_t len = strlen(Letter);
    for(int i= 0;i<len;i++)
      Letter[i] = Letter[i] - 32;
    return Letter;
  }

  virtual ~MakeUpper()
  {
    cout<< "called ~MakeUpper()"<<"\n";
    delete Letter;
  }

private:
  char *Letter;
  bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{
  convertch *makeupper = new MakeUpper("hai"); 
  cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" ";     
  delete makeupper;
  convertch *makelower = new MakeLower("HAI");;
  cout<<"Eneterd : HAI = " <<makelower->convertChar()<<" "; 
  delete makelower;
  return 0;
}

Dari contoh di atas Anda dapat melihat bahwa destruktor untuk kelas MakeUpper dan MakeLower tidak dipanggil.

Lihat contoh berikutnya dengan perusak virtual

#include "stdafx.h"
#include<iostream>

using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
virtual ~convertch(){}; // defined the virtual destructor

};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
      delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] + 32;

}

return Letter;
}

private:
char *Letter;
bool tolower;

};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{

size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] - 32;
}
return Letter;
}
virtual ~MakeUpper()
{
      cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};


int _tmain(int argc, _TCHAR* argv[])
{

convertch *makeupper = new MakeUpper("hai");

cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" \n";

delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<"\n ";


delete makelower;
return 0;
}

Destructor virtual akan memanggil secara eksplisit destructor run time class yang paling banyak sehingga akan dapat membersihkan objek dengan cara yang tepat.

Atau kunjungi tautan

https://web.archive.org/web/20130822173509/http://www.programminggallery.com/article_details.php?article_id=138


1
2017-07-17 14:03