Tuan Lidström dan saya bertengkar :)
Klaim Tn. Lidström adalah bahwa sebuah konstruksi shared_ptr<Base> p(new Derived);
tidak memerlukan Base untuk memiliki penghancur virtual:
Armen Tsirunyan : "Benarkah? Akankah shared_ptr membersihkan dengan benar? Bisakah Anda dalam hal ini menunjukkan bagaimana efek tersebut dapat diterapkan?"
Daniel Lidström : "The shared_ptr menggunakan destruktornya sendiri untuk menghapus instance Concrete. Ini dikenal sebagai RAII dalam komunitas C ++. Saran saya adalah Anda mempelajari semua yang Anda bisa tentang RAII. Ini akan membuat pengkodean C ++ Anda jauh lebih mudah saat Anda menggunakan RAII dalam segala situasi. "
Armen Tsirunyan : "Saya tahu tentang RAII, dan saya juga tahu bahwa pada akhirnya destruktor shared_ptr dapat menghapus piksel yang disimpan saat pn mencapai 0. Tetapi jika px memiliki penunjuk tipe statis ke
Base
dan penunjuk jenis dinamis keDerived
, maka kecualiBase
memiliki destruktor virtual, ini akan mengakibatkan perilaku tidak jelas. Koreksi saya jika saya salah. "Daniel Lidström : "The shared_ptr tahu jenis statisnya adalah Beton. Ia tahu ini sejak saya meluluskannya di konstruktornya! Sepertinya agak ajaib, tapi saya jamin itu sesuai desain dan sangat bagus."
Jadi, nilai kami. Bagaimana mungkin (jika ada) untuk mengimplementasikan shared_ptr tanpa memerlukan kelas polimorfik untuk memiliki destruktor virtual? Terima kasih sebelumnya
sumber
shared_ptr<void> p(new Derived)
juga akan menghancurkanDerived
objek oleh destruktornya, terlepas dari apakah itu benarvirtual
atau tidak.shared_ptr<T>( (T*)new U() )
manastruct U:T
tidak akan melakukan hal yang benar (dan ini dapat dilakukan secara tidak langsung dengan mudah, seperti fungsi yang mengambilT*
dan dilewatkan aU*
)Jawaban:
Ya, itu mungkin untuk mengimplementasikan shared_ptr seperti itu. Boost tidak dan standar C ++ 11 juga memerlukan perilaku ini. Sebagai fleksibilitas tambahan, shared_ptr mengelola lebih dari sekadar penghitung referensi. Penghapus yang disebut biasanya dimasukkan ke dalam blok memori yang sama yang juga berisi penghitung referensi. Tetapi bagian yang menyenangkan adalah tipe dari deleter ini bukan bagian dari tipe shared_ptr. Ini disebut "type erasure" dan pada dasarnya teknik yang sama digunakan untuk mengimplementasikan "fungsi polimorfik" boost :: function atau std :: function untuk menyembunyikan tipe functor yang sebenarnya. Untuk membuat contoh Anda berfungsi, kita membutuhkan konstruktor template:
template<class T> class shared_ptr { public: ... template<class Y> explicit shared_ptr(Y* p); ... };
Jadi, jika Anda menggunakan ini dengan kelas Anda Base dan Derived ...
class Base {}; class Derived : public Base {}; int main() { shared_ptr<Base> sp (new Derived); }
... konstruktor templated dengan Y = Derived digunakan untuk membangun objek shared_ptr. Dengan demikian, konstruktor memiliki kesempatan untuk membuat objek penghapus dan penghitung referensi yang sesuai dan menyimpan penunjuk ke blok kontrol ini sebagai anggota data. Jika penghitung referensi mencapai nol, deleter yang diketahui sebelumnya dan Derived-aware akan digunakan untuk membuang objek.
Standar C ++ 11 mengatakan hal berikut tentang konstruktor ini (20.7.2.2.1):
Dan untuk destruktor (20.7.2.2.2):
(penekanan menggunakan huruf tebal adalah milik saya).
sumber
the upcoming standard also requires this behaviour
: (a) Standar yang mana dan (b) dapatkah Anda memberikan referensi (untuk standar)?add a comment
. IMO, itu lebihBoost does this
darithe Standard requires
. Saya tidak berpikir Standar membutuhkan itu dari apa yang saya pahami. Berbicara tentang contoh @sellibitze 'sshared_ptr<Base> sp (new Derived);
, Memerlukan dariconstructor
hanya memintadelete Derived
menjadi didefinisikan dengan baik dan terbentuk dengan baik. Untuk spesifikasinyadestructor
juga ada ap
, tapi saya rasa tidak mengacup
pada spesifikasi yang adaconstructor
.Ketika shared_ptr dibuat, ia menyimpan objek deleter di dalamnya. Objek ini dipanggil saat shared_ptr akan membebaskan resource yang diarahkan. Karena Anda tahu cara menghancurkan sumber daya pada titik konstruksi, Anda dapat menggunakan shared_ptr dengan tipe yang tidak lengkap. Siapa pun yang membuat shared_ptr menyimpan penghapusan yang benar di sana.
Misalnya, Anda dapat membuat penghapus khusus:
void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed. shared_ptr<Base> p(new Derived, DeleteDerived);
p akan memanggil DeleteDerived untuk menghancurkan objek yang dituju. Implementasinya melakukan ini secara otomatis.
sumber
shared_ptr
sebagai atribut.Secara sederhana,
shared_ptr
menggunakan fungsi deleter khusus yang dibuat oleh konstruktor yang selalu menggunakan destruktor dari objek yang diberikan dan bukan destruktor dari Base, ini sedikit berfungsi dengan pemrograman meta template, tetapi berfungsi.Sesuatu seperti itu
template<typename SomeType> shared_ptr(SomeType *p) { this->destroyer = destroyer_function<SomeType>(p); ... }
sumber