Saya memiliki pemahaman yang kuat tentang sebagian besar teori OO tetapi satu hal yang banyak membingungkan saya adalah penghancur virtual.
Saya pikir destruktor selalu dipanggil tidak peduli apa dan untuk setiap objek dalam rantai.
Kapan Anda dimaksudkan untuk membuatnya virtual dan mengapa?
virtual
pastikan itu dimulai di atas bukan di tengah.Jawaban:
Destructor virtual berguna ketika Anda berpotensi menghapus instance kelas turunan melalui pointer ke kelas dasar:
Di sini, Anda akan melihat bahwa saya tidak menyatakan destructor Base sebagai
virtual
. Sekarang, mari kita lihat cuplikan berikut:Sejak destructor Basis adalah tidak
virtual
danb
adalahBase*
menunjuk keDerived
objek,delete b
memiliki perilaku tidak terdefinisi :Dalam sebagian besar implementasi, panggilan ke destruktor akan diselesaikan seperti kode non-virtual, yang berarti bahwa destruktor dari kelas dasar akan dipanggil tetapi bukan kelas turunan, yang mengakibatkan kebocoran sumber daya.
Singkatnya, selalu membuat destruktor kelas dasar
virtual
ketika mereka dimaksudkan untuk dimanipulasi secara polimorfis.Jika Anda ingin mencegah penghapusan instance melalui pointer kelas dasar, Anda bisa membuat destruktor kelas dasar dilindungi dan nonvirtual; dengan melakukannya, kompiler tidak akan membiarkan Anda memanggil
delete
pointer kelas dasar.Anda dapat mempelajari lebih lanjut tentang virtualitas dan penghancur kelas dasar virtual dalam artikel ini dari Herb Sutter .
sumber
Base
danDerived
memiliki semua variabel penyimpanan otomatis? yaitu tidak ada kode khusus "khusus" atau tambahan untuk dieksekusi di destruktor. Apakah tidak apa-apa untuk berhenti menulis perusak sama sekali? Atau akankah kelas turunannya masih mengalami kebocoran memori?Konstruktor virtual tidak dimungkinkan tetapi destruktor virtual dimungkinkan. Mari kita bereksperimen .......
Kode di atas menghasilkan yang berikut:
Konstruksi objek turunan mengikuti aturan konstruksi tetapi ketika kita menghapus pointer "b" (basis pointer) kita telah menemukan bahwa hanya destruktor basis yang dipanggil. Tetapi ini tidak harus terjadi. Untuk melakukan hal yang sesuai, kita harus membuat basis destructor virtual. Sekarang mari kita lihat apa yang terjadi di bawah ini:
Output berubah sebagai berikut:
Jadi penghancuran penunjuk basis (yang mengambil alokasi pada objek turunan!) Mengikuti aturan penghancuran, yaitu pertama Derived, lalu Base. Di sisi lain, tidak ada yang seperti konstruktor virtual.
sumber
Menyatakan destruktor virtual di kelas dasar polimorfik. Ini adalah Item 7 di C ++ Efektif Scott Meyers ' . Meyers terus meringkas bahwa jika kelas memiliki setiap fungsi virtual, harus memiliki destructor virtual, dan bahwa kelas tidak dirancang untuk menjadi kelas dasar atau tidak dirancang untuk digunakan polymorphically harus tidak menyatakan destructors virtual.
sumber
const Base& = make_Derived();
. Dalam hal ini, destruktor dariDerived
prvalue akan dipanggil, bahkan jika itu bukan virtual, jadi orang menyimpan overhead yang diperkenalkan oleh vtables / vpointers. Tentu saja cakupannya sangat terbatas. Andrei Alexandrescu menyebutkan ini dalam bukunya Modern C ++ Design .Perlu diketahui juga bahwa menghapus pointer kelas dasar ketika tidak ada destruktor virtual akan menghasilkan perilaku yang tidak ditentukan . Sesuatu yang saya pelajari baru-baru ini:
Bagaimana seharusnya menghapus utama di C ++ berperilaku?
Saya telah menggunakan C ++ selama bertahun-tahun dan saya masih bisa menggantung diri.
sumber
Jadikan destruktor virtual kapan pun kelas Anda polimorfik.
sumber
Memanggil destruktor melalui pointer ke kelas dasar
Panggilan destruktor virtual tidak berbeda dari panggilan fungsi virtual lainnya.
Sebab
base->f()
, panggilan akan dikirim keDerived::f()
, dan itu sama untukbase->~Base()
- fungsi utamanya - yangDerived::~Derived()
akan dipanggil.Hal yang sama terjadi ketika destruktor dipanggil secara tidak langsung, mis
delete base;
. Thedelete
pernyataan akan memanggilbase->~Base()
yang akan diberangkatkan keDerived::~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
supaya itu tidak akan dipanggil secara tidak sengaja:sumber
~Derived()
di semua kelas turunan, meskipun itu adil~Derived() = default
? Atau apakah itu tersirat oleh bahasa (membuatnya aman untuk dihilangkan)?protected
bagian, atau untuk memastikan bahwa itu virtual dengan menggunakanoverride
.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 berupa virtual murni. Bagaimana dengan konstruktor? Konstruktor sebenarnya bukan bagian dari antarmuka karena objek selalu dipakai secara eksplisit.
sumber
virtual
di kelas dasar, secara otomatisvirtual
di kelas turunan, bahkan jika tidak dinyatakan demikian.Kata kunci virtual untuk destruktor diperlukan ketika Anda ingin destruktor yang berbeda harus mengikuti urutan yang tepat ketika objek sedang dihapus melalui pointer kelas dasar. sebagai contoh:
Jika destruktor kelas dasar Anda adalah virtual maka objek akan dihancurkan dalam urutan (objek pertama yang diturunkan kemudian basis). Jika destruktor kelas dasar 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.
sumber
Sederhananya, Virtual destructor adalah untuk menghancurkan sumber daya dalam urutan yang tepat, ketika Anda menghapus pointer kelas dasar yang menunjuk ke objek kelas turunan.
sumber
delete
basis pointer mengarah ke perilaku yang tidak ditentukan.Penghancur kelas dasar virtual adalah "praktik terbaik" - Anda harus selalu menggunakannya untuk menghindari kebocoran memori (sulit dideteksi). Dengan menggunakan mereka, Anda dapat yakin bahwa semua destruktor dalam rantai warisan kelas Anda sedang dipanggil (dalam urutan yang benar). Mewarisi dari kelas dasar menggunakan penghancur virtual membuat destruktor dari kelas warisan secara otomatis juga virtual (sehingga Anda tidak perlu mengetik ulang 'virtual' dalam deklarasi destruktor kelas warisan).
sumber
Jika Anda menggunakan
shared_ptr
(hanya shared_ptr, bukan unique_ptr), Anda tidak harus memiliki virtual destructor kelas dasar:keluaran:
sumber
virtual
Kata kunci kecil itu bisa menyelamatkan Anda dari banyak penderitaan.Apa itu destruktor virtual atau cara menggunakan destruktor 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 mengubah huruf ke atas atau bawah
Dari sampel di atas Anda dapat melihat bahwa destruktor untuk kelas MakeUpper dan MakeLower tidak dipanggil.
Lihat contoh selanjutnya dengan destruktor virtual
Destroyer virtual akan memanggil secara eksplisit run time destructor kelas yang paling diturunkan sehingga akan dapat menghapus objek dengan cara yang tepat.
Atau kunjungi tautannya
https://web.archive.org/web/20130822173509/http://www.programminggallery.com/article_details.php?article_id=138
sumber
ketika Anda perlu memanggil destruktor kelas turunan dari kelas dasar. Anda perlu mendeklarasikan destruktor kelas dasar virtual di kelas dasar.
sumber
Saya pikir inti dari pertanyaan ini adalah tentang metode virtual dan polimorfisme, bukan destruktor secara khusus. Ini adalah contoh yang lebih jelas:
Akan dicetak:
Tanpa
virtual
itu akan mencetak:Dan sekarang Anda harus memahami kapan harus menggunakan destruktor virtual.
sumber
B b{}; A& a{b}; a.foo();
. MemeriksaNULL
- yang seharusnyanullptr
- sebelumdelete
- dengan indendasi yang salah - tidak diperlukan:delete nullptr;
didefinisikan sebagai no-op. Jika ada, Anda harus memeriksa ini sebelum memanggil->foo()
, sebagai perilaku dinyatakan tidak terdefinisi dapat terjadi jikanew
entah bagaimana gagal.)delete
padaNULL
pointer (yaitu, Anda tidak perluif (a != NULL)
penjaga).Saya pikir akan bermanfaat untuk membahas perilaku "tidak terdefinisi", atau setidaknya perilaku tidak terdefinisi "crash" yang mungkin terjadi ketika menghapus kelas dasar (/ struct) tanpa destruktor virtual, atau lebih tepatnya tidak ada vtable. Kode di bawah daftar beberapa struct sederhana (hal yang sama berlaku untuk kelas).
Saya tidak menyarankan apakah Anda memerlukan destruktor virtual atau tidak, meskipun saya pikir secara umum itu adalah praktik yang baik untuk memilikinya. Saya hanya menunjukkan alasan Anda mungkin berakhir dengan crash jika kelas dasar Anda (/ struct) tidak memiliki vtable dan kelas turunan Anda (/ struct) tidak dan Anda menghapus objek melalui kelas dasar (/ struct) penunjuk. Dalam hal ini, alamat yang Anda berikan ke rutin bebas heap tidak valid dan dengan demikian alasan kecelakaan itu.
Jika Anda menjalankan kode di atas Anda akan melihat dengan jelas ketika masalah terjadi. Ketika pointer ini dari kelas dasar (/ struct) berbeda dari pointer ini dari kelas turunan (/ struct) Anda akan mengalami masalah ini. Dalam contoh di atas, struct a dan b tidak memiliki vtables. structs c dan d memang memiliki vtables. Dengan demikian pointer a atau b ke instance objek ac atau d akan diperbaiki hingga memperhitungkan vtable. Jika Anda melewatkan a atau b pointer ini untuk menghapusnya akan macet karena alamat tidak valid untuk rutinitas heap gratis.
Jika Anda berencana untuk menghapus turunan instance yang memiliki vtable dari pointer kelas dasar, Anda perlu memastikan kelas dasar memiliki vtable. Salah satu cara untuk melakukannya adalah dengan menambahkan destruktor virtual, yang Anda mungkin ingin membersihkan sumber daya dengan benar.
sumber
Definisi dasar tentang
virtual
ini menentukan apakah fungsi anggota suatu kelas dapat dikuasai secara berlebihan dalam kelas turunannya.D-tor kelas pada dasarnya disebut di akhir ruang lingkup, tetapi ada masalah, misalnya ketika kita mendefinisikan instance pada Heap (alokasi dinamis), kita harus menghapusnya secara manual.
Segera setelah instruksi dieksekusi, destructor kelas dasar dipanggil, tetapi tidak untuk yang diturunkan.
Contoh praktis adalah ketika, di bidang kontrol, Anda harus memanipulasi efektor, aktuator.
Pada akhir ruang lingkup, jika penghancur salah satu elemen daya (Aktuator), tidak dipanggil, akan ada konsekuensi fatal.
sumber
Setiap kelas yang diwariskan kepada publik, polimorfik atau tidak, harus memiliki destruktor virtual. Dengan kata lain, jika dapat ditunjukkan oleh pointer kelas dasar, kelas dasarnya harus memiliki destruktor virtual.
Jika virtual, destruktor kelas turunan dipanggil, maka konstruktor kelas dasar. Jika tidak virtual, hanya destructor kelas dasar yang dipanggil.
sumber