Pertanyaan shared_ptr magic :)


Mr. Lidström dan saya bertengkar :)

Klaim Pak Lidström adalah sebuah gagasan shared_ptr<Base> p(new Derived); tidak memerlukan Basis untuk memiliki perusak virtual:

Armen Tsirunyan: "Benarkah? Akankah shared_ptr bersihkan dengan benar? Bisakah Anda dalam kasus ini menunjukkan bagaimana efek itu bisa diimplementasikan? "

Daniel Lidström: "The shared_ptr menggunakan destructor sendiri untuk menghapus instance konkret. Ini dikenal sebagai RAII dalam komunitas C ++. Saran saya adalah Anda mempelajari semua yang Anda bisa tentang RAII. Ini akan membuat coding C ++ Anda jadi lebih mudah ketika Anda menggunakan RAII dalam semua situasi. "

Armen Tsirunyan: "Saya tahu tentang RAII, dan saya juga tahu bahwa pada akhirnya shared_ptr destructor dapat menghapus px yang tersimpan ketika pn mencapai 0. Tetapi jika px memiliki pointer tipe statis Base dan pointer tipe dinamis ke Derived, kecuali jika Base memiliki destruktor virtual, ini akan menghasilkan perilaku tidak terdefinisi. Koreksi saya jika saya salah. "

Daniel Lidström: "The shared_ptr tahu tipe statis adalah Beton. Ia tahu ini sejak saya lulus dalam konstruktornya! Terlihat sedikit seperti sulap, tapi saya dapat meyakinkan Anda bahwa itu dengan desain dan sangat bagus. "

Jadi, hitung kami. Bagaimana mungkin (jika) untuk diimplementasikan shared_ptr tanpa membutuhkan kelas polimorfik untuk memiliki destruktor virtual? Terima kasih sebelumnya


76
2017-10-10 09:41


asal


Jawaban:


Ya, mungkin untuk menerapkan shared_ptr seperti itu. Boost tidak dan standar C ++ 11 juga membutuhkan perilaku ini. Sebagai keluwesan 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 bahwa jenis deleter ini bukan bagian dari tipe shared_ptr. Ini disebut "penghapusan jenis" dan pada dasarnya adalah teknik yang sama yang digunakan untuk mengimplementasikan "fungsi polimorfik" boost :: function atau std :: function untuk menyembunyikan tipe functor yang sebenarnya. Untuk membuat contoh Anda berfungsi, kami memerlukan konstruktor templated:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

Jadi, jika Anda menggunakan ini dengan Basis kelas Anda dan Berasal ...

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. Konstruktor memiliki kesempatan untuk membuat objek deleter dan penghitung referensi yang tepat dan menyimpan pointer ke blok kontrol ini sebagai anggota data. Jika penghitung referensi mencapai nol, deleter yang dibuat sebelumnya dan Derived-aware akan digunakan untuk membuang objek.

Standar C ++ 11 memiliki hal berikut untuk dikatakan tentang konstruktor ini (20.7.2.2.1):

Membutuhkan:  p harus dapat dikonversi ke T*. Y akan menjadi tipe lengkap. Ekspresi delete p harus terbentuk dengan baik, harus memiliki perilaku yang terdefinisi dengan baik dan tidak akan melempar pengecualian.

Efek: Membangun sebuah shared_ptr obyek bahwa memiliki penunjuk p.

...

Dan untuk destructor (20.7.2.2.2):

Efek: Jika *this aku s kosong atau berbagi kepemilikan dengan yang lain shared_ptr contoh (use_count() > 1), tidak ada efek samping.   Kalau tidak, jika *this memiliki sebuah objek p dan sebuah deleter d, d(p) disebut.    Kalau tidak, jika *this memiliki penunjuk p, dan delete p disebut.

(Penekanan menggunakan huruf tebal adalah milik saya).


67
2017-10-10 11:18



Ketika shared_ptr dibuat, ia menyimpan deleter objek di dalam dirinya. Objek ini dipanggil ketika shared_ptr akan membebaskan sumber daya yang runcing. Karena Anda tahu cara menghancurkan sumber daya pada titik konstruksi, Anda dapat menggunakan shared_ptr dengan jenis yang tidak lengkap. Siapa pun yang membuat shared_ptr menyimpan deleter yang benar di sana.

Misalnya, Anda dapat membuat deleter 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 runcing. Implementasinya melakukan ini secara otomatis.


26
2017-10-10 09:47



Secara sederhana,

shared_ptr menggunakan fungsi deleter khusus yang dibuat oleh konstruktor yang selalu menggunakan destruktor dari objek yang diberikan dan bukan destruktor Base, ini adalah sedikit kerja dengan pemrograman meta template, tetapi itu berhasil.

Sesuatu seperti itu

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

14
2017-10-10 09:46