Saya memiliki pemahaman yang solid dari yang paling OO teori tapi satu hal yang membingungkan saya adalah virtual destruktor.
Saya berpikir bahwa destructor selalu dipanggil tidak peduli apa dan untuk setiap objek dalam rantai.
Ketika anda dimaksudkan untuk membuat mereka virtual dan mengapa?
Virtual destruktor berguna ketika anda mungkin berpotensi menghapus sebuah instance dari sebuah kelas yang diturunkan melalui pointer ke kelas dasar:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
Di sini, anda'll pemberitahuan bahwa saya tidak't menyatakan Dasar's destructor untuk menjadi virtual
. Sekarang, let's silahkan lihat pada cuplikan berikut:
Base *b = new Derived();
// use b
delete b; // Here's the problem!
Karena Dasar's destructor tidak virtual
dan b
adalah Dasar*
menunjuk Berasal
objek delete b
telah undefined perilaku:
[
hapus
b], jika statis jenis objek yang akan dihapus adalah berbeda dari tipe dinamis, statis jenis akan menjadi dasar kelas dinamis jenis objek yang akan dihapus dan statis jenis akan memiliki virtual destructor atau perilaku yang tidak terdefinisi.
Di sebagian besar implementasi, panggilan untuk destructor akan diselesaikan seperti non-virtual kode, yang berarti bahwa destruktor dari kelas dasar akan dipanggil tetapi tidak salah satu dari kelas yang diturunkan, sehingga sumber-sumber kebocoran.
Untuk meringkas, selalu membuat kelas dasar' destruktor virtual
ketika mereka're dimaksudkan untuk dimanipulasi polymorphically.
Jika anda ingin mencegah penghapusan sebuah instance melalui kelas dasar pointer, anda dapat membuat kelas dasar destructor dilindungi dan nonvirtual; dengan demikian, compiler tidak't membiarkan anda menelepon delete
pada kelas dasar pointer.
Anda dapat mempelajari lebih lanjut tentang dunia maya dan virtual base kelas destructor di artikel ini dari Ramuan Sutter.
Virtual constructor tidak mungkin tapi virtual destructor adalah mungkin. 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 output berikut:
Base Constructor Called
Derived constructor called
Base Destructor called
Pembangunan yang berasal objek mengikuti aturan konstruksi tetapi ketika kita menghapus "b" pointer(base pointer) kami telah menemukan bahwa hanya dasar destructor dipanggil. Tapi ini tidak harus terjadi. Untuk melakukan hal yang tepat, kita harus membuat basis virtual destructor. 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
Sehingga kerusakan base pointer (yang membutuhkan alokasi pada berasal objek!) berikut kehancuran aturan, saya.e pertama yang Diturunkan, maka Dasar. Di sisi lain, tidak ada yang seperti virtual constructor.
Menyatakan destruktor virtual di polimorfik kelas dasar. Ini adalah Item 7 di Scott Meyers' Efektif C++. Meyers masuk menyimpulkan bahwa jika sebuah class memiliki setiap fungsi virtual, harus memiliki virtual destructor, dan bahwa kelas tidak dirancang untuk menjadi dasar kelas atau tidak dirancang untuk digunakan polymorphically harus tidak menyatakan virtual destruktor.
Juga perlu diketahui bahwa menghapus kelas dasar pointer ketika tidak ada virtual destructor akan mengakibatkan undefined perilaku. Sesuatu yang saya pelajari baru-baru ini:
https://stackoverflow.com/questions/408196/how-should-overriding-delete-in-c-behave
I've telah menggunakan C++ selama bertahun-tahun dan saya masih mengelola untuk menggantung diri.
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
Virtual destructor panggilan tidak berbeda dari yang lain maya panggilan fungsi.
Untuk base->f()
, panggilan akan dikirim ke Berasal::f()
, dan's yang sama untuk dasar->~Dasar()- nya meng-override fungsi -
Berasal::~Turunan()` akan disebut.
Yang sama terjadi ketika destructor dipanggil secara tidak langsung, misalnya menghapus dasar;
. Hapus
pernyataan akan memanggil base->~Dasar()
yang akan diberangkatkan ke Berasal::~Turunan()
.
Jika anda tidak akan menghapus objek melalui pointer ke kelas dasar - maka tidak ada kebutuhan untuk memiliki sebuah virtual destructor. Hanya membuat dilindungi
sehingga tidak't disebut 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.
}
Saya suka berpikir tentang interface dan implementasi antarmuka. Dalam C++ berbicara antarmuka virtual murni kelas. Destructor adalah bagian dari antarmuka dan diharapkan untuk dilaksanakan. Oleh karena itu destructor harus murni virtual. Bagaimana dengan constructor? Konstruktor adalah benar-benar bukan bagian dari antarmuka karena objek selalu diturunkan secara eksplisit.
Untuk menjadi sederhana, Virtual destructor adalah untuk merusak sumber daya dalam urutan yang tepat, pada saat anda menghapus kelas dasar pointer yang menunjuk ke objek kelas turunan.
#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
Kata kunci Virtual untuk destructor adalah diperlukan ketika anda ingin berbeda destruktor harus mengikuti urutan yang tepat saat objek sedang dihapus melalui kelas dasar pointer. misalnya:
Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ;
Jika anda berasal dari kelas destructor adalah virtual maka benda akan destrcuted dalam rangka(pertama berasal objek maka basis ). Jika anda berasal dari kelas destructor TIDAK virtual maka hanya dasar kelas objek yang akan dihapus(karena pointer dari kelas dasar "Dasar *myObj"). Jadi akan ada kebocoran memori untuk yang berasal objek.
Apa itu virtual destructor atau cara menggunakan virtual destructor
Kelas destruktor adalah fungsi dengan nama yang sama dari kelas sebelumnya dengan ~ yang akan mengalokasikan memori yang dialokasikan oleh kelas. Mengapa kita perlu virtual destructor
Lihat contoh berikut dengan beberapa fungsi virtual
Sampel juga memberitahu bagaimana anda dapat mengkonversi surat 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 destructor untuk investasi MakeUpper dan MakeLower kelas tidak disebut.
Lihat sampel berikutnya dengan virtual destructor
#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;
}
Virtual destructor akan memanggil secara eksplisit sebagian besar berasal jangka waktu destruktor dari kelas sehingga akan mampu untuk menghapus objek dalam cara yang tepat.
Atau kunjungi link
Virtual dasar kelas destruktor adalah "praktek terbaik" - anda harus selalu menggunakan mereka untuk menghindari (sulit untuk mendeteksi) kebocoran memori. Menggunakan mereka, anda dapat yakin semua destruktor dalam rantai warisan dari kelas anda yang beeing disebut (dalam urutan yang tepat). Mewarisi dari kelas dasar menggunakan virtual destructor membuat destruktor dari mewarisi kelas secara otomatis virtual, juga (jadi anda tidak perlu mengetik ulang 'maya' dalam mewarisi kelas destructor deklarasi).
Jika anda menggunakan shared_ptr
(hanya shared_ptr, tidak unique_ptr), anda don't harus memiliki dasar kelas virtual destructor:
``
menggunakan namespace std;
kelas Dasar { umum: Base(){ cout << "Dasar Konstruktor yang Disebut\n"; } ~Dasar(){ // tidak virtual cout << "Dasar Destructor dipanggil\n"; } };
kelas Turunan: publik Dasar { umum: Berasal(){ cout << "Berasal konstruktor yang disebut\n"; } ~Turunan(){ cout << "Berasal destructor dipanggil\n"; } };
int main()
{
shared_ptr
Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called
Saya pikir itu akan bermanfaat untuk mendiskusikan "undefined" perilaku, atau setidaknya "crash" undefined perilaku yang mungkin terjadi saat menghapus melalui kelas dasar(/struct) tanpa virtual destructor, atau lebih tepatnya tidak ada vtable. Kode di bawah ini daftar beberapa sederhana struct (hal yang sama akan berlaku untuk kelas).
#include <iostream>
using namespace std;
struct a
{
~a() {}
unsigned long long i;
};
struct b : a
{
~b() {}
unsigned long long j;
};
struct c : b
{
~c() {}
virtual void m3() {}
unsigned long long k;
};
struct d : c
{
~d() {}
virtual void m4() {}
unsigned long long l;
};
int main()
{
cout << "sizeof(a): " << sizeof(a) << endl;
cout << "sizeof(b): " << sizeof(b) << endl;
cout << "sizeof(c): " << sizeof(c) << endl;
cout << "sizeof(d): " << sizeof(d) << endl;
// No issue.
a* a1 = new a();
cout << "a1: " << a1 << endl;
delete a1;
// No issue.
b* b1 = new b();
cout << "b1: " << b1 << endl;
cout << "(a*) b1: " << (a*) b1 << endl;
delete b1;
// No issue.
c* c1 = new c();
cout << "c1: " << c1 << endl;
cout << "(b*) c1: " << (b*) c1 << endl;
cout << "(a*) c1: " << (a*) c1 << endl;
delete c1;
// No issue.
d* d1 = new d();
cout << "d1: " << d1 << endl;
cout << "(c*) d1: " << (c*) d1 << endl;
cout << "(b*) d1: " << (b*) d1 << endl;
cout << "(a*) d1: " << (a*) d1 << endl;
delete d1;
// Doesn't crash, but may not produce the results you want.
c1 = (c*) new d();
delete c1;
// Crashes due to passing an invalid address to the method which
// frees the memory.
d1 = new d();
b1 = (b*) d1;
cout << "d1: " << d1 << endl;
cout << "b1: " << b1 << endl;
delete b1;
/*
// This is similar to what's happening above in the "crash" case.
char* buf = new char[32];
cout << "buf: " << (void*) buf << endl;
buf += 8;
cout << "buf after adding 8: " << (void*) buf << endl;
delete buf;
*/
}
I'm tidak menunjukkan apakah anda membutuhkan virtual destruktor atau tidak, meskipun saya pikir secara umum's praktik yang baik untuk memiliki mereka. I'm hanya menunjukkan alasan anda mungkin berakhir dengan kecelakaan jika anda kelas dasar(/struct) tidak memiliki vtable dan kelas turunan(/struct) dan anda menghapus sebuah objek melalui kelas dasar(/struct) pointer. Dalam kasus ini, alamat anda lulus ke tumpukan's gratis rutin adalah tidak sah dan dengan demikian alasan untuk kecelakaan.
Jika anda menjalankan kode diatas anda'll melihat dengan jelas ketika masalah ini terjadi. Ketika pointer ini dari kelas dasar(/struct) adalah berbeda dari pointer ini dari kelas yang diturunkan(/struct) anda'kembali pergi untuk menjalankan ke dalam masalah ini. Dalam contoh di atas, struct a dan b don't memiliki vtables. struct c dan d memiliki vtables. Dengan demikian a atau b pointer ke c atau d contoh objek akan tetap up untuk account untuk vtable. Jika anda lulus ini a atau b pointer hapus akan crash karena alamat yang tidak valid untuk heap's gratis rutin.
Jika anda berencana untuk menghapus berasal contoh yang telah vtables dari kelas dasar pointer, anda perlu memastikan dasar kelas memiliki vtable. Salah satu cara untuk melakukannya adalah untuk menambahkan virtual destructor, yang mungkin anda ingin pula untuk benar membersihkan sumber daya.
Saya pikir inti dari pertanyaan ini adalah tentang metode virtual dan polimorfisme, tidak destructor khusus. Berikut ini adalah contoh lebih jelas:
class A
{
public:
A() {}
virtual void foo()
{
cout << "This is A." << endl;
}
};
class B : public A
{
public:
B() {}
void foo()
{
cout << "This is B." << endl;
}
};
int main(int argc, char* argv[])
{
A *a = new B();
a->foo();
if(a != NULL)
delete a;
return 0;
}
Akan mencetak:
This is B.
Tanpa virtual
ini akan mencetak:
This is A.
Dan sekarang anda harus memahami kapan harus menggunakan virtual destruktor.
Setiap kelas yang diwariskan secara terbuka, polimorfik atau tidak, harus memiliki virtual destructor. Dengan kata lain, jika hal ini dapat ditunjukkan oleh kelas dasar pointer, basis kelas harus memiliki sebuah virtual destructor.
Jika virtual, kelas yang diturunkan destructor dipanggil, maka dasar kelas konstruktor. Jika tidak virtual, hanya dasar kelas destructor dipanggil.