Pertanyaan Dasar: kapan sebuah program memanggil metode destruktor kelas di C ++? Saya telah diberitahu bahwa itu dipanggil setiap kali sebuah benda keluar dari ruang lingkup atau dikenakandelete
Pertanyaan yang lebih spesifik:
1) Jika objek dibuat melalui pointer dan pointer itu kemudian dihapus atau diberi alamat baru untuk ditunjuk, apakah objek yang ditunjuk untuk memanggil destruktornya (dengan asumsi tidak ada lagi yang menunjuk ke sana)?
2) Menindaklanjuti pertanyaan 1, apa yang mendefinisikan ketika sebuah objek keluar dari ruang lingkup (bukan mengenai kapan sebuah objek meninggalkan {blok} tertentu). Jadi, dengan kata lain, kapan destruktor dipanggil pada objek dalam daftar tertaut?
3) Apakah Anda pernah ingin memanggil destruktor secara manual?
sumber
Jawaban:
Itu tergantung pada jenis penunjuknya. Misalnya, penunjuk cerdas sering kali menghapus objeknya saat dihapus. Petunjuk biasa tidak. Hal yang sama juga berlaku saat pointer dibuat untuk menunjuk ke objek yang berbeda. Beberapa petunjuk cerdas akan menghancurkan objek lama, atau akan menghancurkannya jika tidak ada lagi referensi. Petunjuk biasa tidak memiliki kecerdasan seperti itu. Mereka hanya menyimpan alamat dan memungkinkan Anda untuk melakukan operasi pada objek yang mereka tunjuk dengan melakukannya secara khusus.
Itu terserah penerapan daftar tertaut. Koleksi tipikal menghancurkan semua objek yang dikandungnya saat dihancurkan.
Jadi, daftar penunjuk yang ditautkan biasanya akan menghancurkan penunjuk tetapi bukan objek yang mereka tunjuk. (Yang mungkin benar. Mereka mungkin referensi oleh penunjuk lain.) Sebuah daftar tertaut yang dirancang khusus untuk berisi penunjuk, bagaimanapun, mungkin menghapus objek pada penghancurannya sendiri.
Daftar penunjuk cerdas yang ditautkan dapat secara otomatis menghapus objek saat penunjuk dihapus, atau melakukannya jika tidak ada lagi referensi. Terserah Anda untuk memilih bagian yang melakukan apa yang Anda inginkan.
Tentu. Salah satu contohnya adalah jika Anda ingin mengganti objek dengan objek lain yang berjenis sama tetapi tidak ingin mengosongkan memori hanya untuk mengalokasikannya lagi. Anda dapat menghancurkan objek lama di tempatnya dan membangun yang baru di tempatnya. (Namun, secara umum ini adalah ide yang buruk.)
sumber
new Foo()
dengan huruf besar 'F'.)Foo myfoo("foo")
bukan Most Vexing Parse, tapichar * foo = "foo"; Foo myfoo(foo);
sebenarnya.delete myFoo
dipanggil sebelumnyaFoo *myFoo = new Foo("foo");
? Atau Anda akan menghapus objek yang baru dibuat, bukan?myFoo
sebelumFoo *myFoo = new Foo("foo");
garis. Baris itu membuat variabel baru yang disebutmyFoo
, membayangi variabel yang sudah ada. Padahal dalam kasus ini, tidak ada yang ada karena hal dimyFoo
atas termasuk dalam ruang lingkupif
, yang sudah berakhir.Orang lain telah membahas masalah lain, jadi saya hanya akan melihat satu hal: apakah Anda pernah ingin menghapus objek secara manual.
Jawabannya iya. @DavidSchwartz memberi satu contoh, tapi itu cukup satu tidak biasa. Saya akan memberikan contoh yang ada di balik terpal dari apa yang banyak digunakan programmer C ++ sepanjang waktu:
std::vector
(danstd::deque
, meskipun tidak terlalu banyak digunakan).Seperti yang diketahui kebanyakan orang,
std::vector
akan mengalokasikan blok memori yang lebih besar ketika / jika Anda menambahkan lebih banyak item daripada yang dapat ditampung oleh alokasi saat ini. Namun, ketika melakukan ini, ia memiliki blok memori yang mampu menampung lebih banyak objek daripada yang ada di vektor.Untuk mengelolanya, apa yang
vector
dilakukan di bawah selimut adalah mengalokasikan memori mentah melaluiAllocator
objek (yang, kecuali Anda menentukan sebaliknya, berarti digunakannya::operator new
). Kemudian, saat Anda menggunakan (misalnya)push_back
untuk menambahkan item kevector
, secara internal vektor menggunakan aplacement new
untuk membuat item di bagian ruang memorinya (yang sebelumnya) tidak terpakai.Sekarang, apa yang terjadi jika / jika Anda
erase
merupakan item dari vektor? Itu tidak bisa begitu sajadelete
- itu akan melepaskan seluruh blok memorinya; ia perlu menghancurkan satu objek dalam memori itu tanpa merusak yang lain, atau melepaskan salah satu blok memori yang dikendalikannya (misalnya, jika Andaerase
5 item dari vektor, lalu segerapush_back
5 item lagi, dijamin bahwa vektor tidak akan dialokasikan kembali memori saat Anda melakukannya.Untuk melakukan itu, vektor secara langsung menghancurkan objek dalam memori dengan secara eksplisit memanggil destruktor, bukan dengan menggunakan
delete
.Jika, mungkin, ada orang lain yang menulis wadah menggunakan penyimpanan yang berdekatan secara kasar seperti yang
vector
dilakukan (atau beberapa varian dari itu, seperti yangstd::deque
sebenarnya), Anda hampir pasti ingin menggunakan teknik yang sama.Sebagai contoh, mari pertimbangkan bagaimana Anda dapat menulis kode untuk buffer cincin melingkar.
Berbeda dengan wadah standar, ini menggunakan
operator new
danoperator delete
langsung. Untuk penggunaan nyata, Anda mungkin ingin menggunakan kelas pengalokasi, tetapi untuk saat ini akan melakukan lebih banyak hal untuk mengalihkan perhatian daripada berkontribusi (bagaimanapun juga, IMO).sumber
new
, Anda bertanggung jawab untuk memanggildelete
. Saat Anda membuat objek denganmake_shared
, hasilnyashared_ptr
bertanggung jawab untuk menghitung dan memanggildelete
saat jumlah penggunaan mencapai nol.new
(yaitu, itu adalah objek tumpukan).new
.sumber
1) Objek tidak dibuat 'melalui pointer'. Ada penunjuk yang ditugaskan ke objek apa pun yang Anda 'baru'. Dengan asumsi ini yang Anda maksud, jika Anda memanggil 'hapus' pada penunjuk, itu benar-benar akan menghapus (dan memanggil destruktor pada) objek dereferensi penunjuk. Jika Anda menetapkan pointer ke objek lain, akan ada kebocoran memori; tidak ada di C ++ yang akan mengumpulkan sampah untuk Anda.
2) Ini adalah dua pertanyaan terpisah. Sebuah variabel keluar dari ruang lingkup ketika stack frame yang dideklarasikannya dikeluarkan dari stack. Biasanya ini adalah saat Anda meninggalkan blok. Objek di tumpukan tidak pernah keluar dari ruang lingkup, meskipun mungkin penunjuk mereka di tumpukan. Tidak ada yang secara khusus menjamin bahwa destruktor dari suatu objek dalam daftar tertaut akan dipanggil.
3) Tidak juga. Mungkin ada Sihir Ajaib yang menyarankan sebaliknya, tetapi biasanya Anda ingin mencocokkan kata kunci 'baru' dengan kata kunci 'hapus', dan memasukkan semua yang diperlukan ke dalam destruktor untuk memastikan kata kunci 'baru' itu bersih sendiri. Jika Anda tidak melakukan ini, pastikan untuk mengomentari destruktor dengan instruksi khusus kepada siapa pun yang menggunakan kelas tentang bagaimana mereka harus membersihkan sumber daya objek itu secara manual.
sumber
Untuk memberikan jawaban rinci atas pertanyaan 3: ya, ada (jarang) saat Anda mungkin memanggil destruktor secara eksplisit, khususnya sebagai mitra untuk penempatan baru, seperti yang diamati dasblinkenlight.
Untuk memberikan contoh konkret tentang ini:
Tujuan dari hal semacam ini adalah untuk memisahkan alokasi memori dari konstruksi objek.
sumber
Pointer - Pointer biasa tidak mendukung RAII. Tanpa eksplisit
delete
, akan ada sampah. Untungnya C ++ memiliki petunjuk otomatis yang menangani ini untuk Anda!Cakupan - Pikirkan saat variabel menjadi tidak terlihat oleh program Anda. Biasanya ini di akhir
{block}
, seperti yang Anda tunjukkan.Penghancuran manual - Jangan pernah mencoba ini. Biarkan scope dan RAII melakukan keajaiban untuk Anda.
sumber
std::auto_ptr
tidak digunakan lagi di C ++ 11, ya. Jika OP benar-benar memiliki C ++ 11, ia harus menggunakanstd::unique_ptr
untuk pemilik tunggal, ataustd::shared_ptr
untuk beberapa pemilik yang dihitung referensi.std::queue<std::shared_ptr>?
saya telah menemukan bahwapipe()
antara utas produsen dan konsumen membuat konkurensi jauh lebih mudah, jika penyalinan tidak terlalu mahal.Setiap kali Anda menggunakan "baru", yaitu, melampirkan alamat ke penunjuk, atau mengatakan, Anda mengklaim ruang di heap, Anda perlu "menghapusnya".
1. ya, saat Anda menghapus sesuatu, destruktor dipanggil.
2. Saat destruktor dari daftar tertaut dipanggil, destruktor objeknya dipanggil. Tetapi jika itu adalah pointer, Anda perlu menghapusnya secara manual. 3. saat ruang diklaim "baru".
sumber
Ya, destruktor (alias dtor) dipanggil saat sebuah objek keluar dari ruang lingkup jika berada di tumpukan atau saat Anda memanggil
delete
penunjuk ke sebuah objek.Jika pointer dihapus melalui
delete
maka dtor akan dipanggil. Jika Anda menetapkan ulang pointer tanpa memanggildelete
terlebih dahulu, Anda akan mendapatkan kebocoran memori karena objek tersebut masih ada di memori di suatu tempat. Dalam contoh terakhir, dtor tidak dipanggil.Implementasi daftar tertaut yang baik akan memanggil dtor dari semua objek dalam daftar ketika daftar sedang dihancurkan (karena Anda memanggil beberapa metode untuk menghancurkannya atau keluar dari ruang lingkup itu sendiri). Ini bergantung pada implementasi.
Saya meragukannya, tetapi saya tidak akan terkejut jika ada beberapa keadaan aneh di luar sana.
sumber
Jika objek dibuat tidak melalui pointer (misalnya, A a1 = A ();), destruktor dipanggil saat objek dihancurkan, selalu saat fungsi tempat objek itu berada selesai. Misalnya:
destruktor dipanggil ketika kode dieksekusi ke baris "selesai".
Jika objek dibuat melalui pointer (misalnya, A * a2 = new A ();), destruktor dipanggil ketika pointer dihapus (hapus a2;). Jika titik tidak dihapus oleh pengguna secara eksplisit atau diberi alamat baru sebelum menghapusnya, terjadi kebocoran memori. Itu adalah bug.
Dalam daftar tertaut, jika kita menggunakan std :: list <>, kita tidak perlu peduli dengan desctructor atau kebocoran memori karena std :: list <> telah menyelesaikan semua ini untuk kita. Dalam daftar tertaut yang kita tulis sendiri, kita harus menulis desctructor dan menghapus penunjuk secara eksplisit, jika tidak, akan menyebabkan kebocoran memori.
Kami jarang memanggil destruktor secara manual. Ini adalah fungsi yang menyediakan sistem.
Maaf untuk bahasa Inggris saya yang buruk!
sumber
Ingatlah bahwa Pembuat suatu objek dipanggil segera setelah memori dialokasikan untuk objek itu dan sedangkan destruktor dipanggil tepat sebelum mengalihkan memori objek itu.
sumber