Ini memungkinkan Anda untuk mendapatkan shared_ptr
contoh yang valid untuk this
, ketika semua yang Anda miliki adalah this
. Tanpa itu, Anda akan memiliki cara untuk mendapatkan shared_ptr
untuk this
, kecuali jika Anda sudah memiliki satu sebagai anggota. Contoh ini dari dokumentasi boost untuk enable_shared_from_ini :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Metode f()
mengembalikan yang valid shared_ptr
, meskipun tidak memiliki turunan anggota. Perhatikan bahwa Anda tidak bisa begitu saja melakukan ini:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Pointer bersama yang dikembalikan ini akan memiliki jumlah referensi yang berbeda dari yang "tepat", dan salah satu dari mereka akan kehilangan dan menahan referensi yang menggantung ketika objek dihapus.
enable_shared_from_this
telah menjadi bagian dari standar C ++ 11. Anda juga bisa mendapatkannya dari sana juga dari boost.
std::shared_ptr
konstruktor pada pointer mentah jika diwarisi daristd::enable_shared_from_this
. Saya tidak tahu apakah semantik Boost diperbarui untuk mendukung ini.std::shared_ptr
untuk objek yang sudah dikelola oleh orang lainstd::shared_ptr
tidak akan berkonsultasi dengan referensi lemah yang disimpan secara internal dan dengan demikian akan menyebabkan perilaku yang tidak terdefinisi." ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )shared_ptr<Y> q = p
?std::make_shared<T>
.dari artikel Dr Dobbs tentang pointer lemah, saya pikir contoh ini lebih mudah dimengerti (sumber: http://drdobbs.com/cpp/184402026 ):
... kode seperti ini tidak akan berfungsi dengan benar:
Tak satu pun dari dua
shared_ptr
objek tahu tentang yang lain, sehingga keduanya akan mencoba melepaskan sumber daya ketika mereka dihancurkan. Itu biasanya mengarah pada masalah.Demikian pula, jika fungsi anggota memerlukan
shared_ptr
objek yang memiliki objek yang dipanggil, ia tidak bisa hanya membuat objek dengan cepat:Kode ini memiliki masalah yang sama dengan contoh sebelumnya, meskipun dalam bentuk yang lebih halus. Ketika dibangun,
shared_pt
objek rsp1
memiliki sumber daya yang baru dialokasikan. Kode di dalam fungsi anggotaS::dangerous
tidak tahu tentangshared_ptr
objek itu, jadishared_ptr
objek yang dikembalikannya berbedasp1
. Menyalinshared_ptr
objek baru kesp2
tidak membantu; ketikasp2
keluar dari ruang lingkup, itu akan melepaskan sumber daya, dan ketikasp1
keluar dari ruang lingkup, itu akan melepaskan sumber daya lagi.Cara untuk menghindari masalah ini adalah dengan menggunakan templat kelas
enable_shared_from_this
. Templat mengambil satu argumen jenis templat, yang merupakan nama kelas yang menentukan sumber daya yang dikelola. Kelas itu harus, pada gilirannya, diturunkan secara publik dari templat; seperti ini:Ketika Anda melakukan ini, perlu diingat bahwa objek yang Anda panggil
shared_from_this
harus dimiliki oleh suatushared_ptr
objek. Ini tidak akan berfungsi:sumber
shared_ptr<S> sp1(new S);
lebih disukai untuk digunakanshared_ptr<S> sp1 = make_shared<S>();
, lihat misalnya stackoverflow.com/questions/18301511/…shared_ptr<S> sp2 = p->not_dangerous();
karena perangkap di sini adalah Anda harus membuat shared_ptr dengan cara yang normal sebelum Anda meneleponshared_from_this()
pertama kali! Ini sangat mudah salah! Sebelum C ++ 17, ini adalah UB untuk memanggilshared_from_this()
sebelum tepat satu shared_ptr telah dibuat dengan cara biasa:auto sptr = std::make_shared<S>();
ataushared_ptr<S> sptr(new S());
. Untungnya dari C ++ 17 dan seterusnya akan melempar.S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- Diijinkan untuk memanggil shared_from_ini hanya pada objek yang dibagikan sebelumnya, yaitu pada objek yang dikelola oleh std :: shared_ptr <T>. Kalau tidak, perilaku tidak terdefinisi (sampai C ++ 17) std :: bad_weak_ptr dilemparkan (oleh constructor shared_ptr dari yang dibangun-lemah, lemah_ini) (sejak C ++ 17). . Jadi kenyataannya adalah bahwa itu harus dipanggilalways_dangerous()
, karena Anda perlu pengetahuan apakah sudah dibagikan atau belum.Inilah penjelasan saya, dari perspektif mur dan baut (jawaban teratas tidak 'klik' dengan saya). * Perhatikan bahwa ini adalah hasil dari penyelidikan sumber untuk shared_ptr dan enable_shared_from_ini yang datang dengan Visual Studio 2012. Mungkin kompiler lain mengimplementasikan enable_shared_from_this berbeda ... *
enable_shared_from_this<T>
menambahkanweak_ptr<T>
contoh pribadiT
yang memegang ' satu hitungan referensi yang benar ' untuk contohT
.Jadi, ketika Anda pertama kali membuat a
shared_ptr<T>
ke T * baru, itu melemah_ptr internal T * akan diinisialisasi dengan refcount dari 1. Yang barushared_ptr
pada dasarnya mendukung iniweak_ptr
.T
kemudian dapat, dalam metodenya, panggilanshared_from_this
untuk mendapatkan sebuah instance darishared_ptr<T>
yang mendukung jumlah referensi yang disimpan secara internal yang sama . Dengan cara ini, Anda selalu memiliki satu tempat menyimpanT*
penghitungan ulang alih-alih memiliki banyakshared_ptr
contoh yang tidak saling mengenal, dan masing-masing menganggap merekashared_ptr
yang bertanggung jawab menghitung ulangT
dan menghapusnya saat ref -hitung mencapai nol.sumber
So, when you first create...
karena itu adalah persyaratan (seperti yang Anda katakan lemah_ptr tidak diinisialisasi sampai Anda meneruskan pointer objek ke dalam ctor shared_ptr!) Dan persyaratan ini adalah di mana segala sesuatu bisa menjadi sangat salah jika Anda kurang teliti. Jika Anda tidak membuat shared_ptr sebelum memanggilshared_from_this
Anda mendapatkan UB - demikian juga jika Anda membuat lebih dari satu shared_ptr, Anda juga mendapatkan UB. Anda harus entah bagaimana memastikan Anda membuat shared_ptr tepat sekali.enable_shared_from_this
rapuh untuk memulai karena intinya adalah untuk bisa mendapatkanshared_ptr<T>
dariT*
, tetapi pada kenyataannya ketika Anda mendapatkan pointerT* t
, umumnya tidak aman untuk menganggap apa pun tentang itu sudah dibagikan atau tidak, dan membuat tebakan yang salah adalah UB.Perhatikan bahwa menggunakan boost :: intrusive_ptr tidak mengalami masalah ini. Ini sering merupakan cara yang lebih mudah untuk mengatasi masalah ini.
sumber
enable_shared_from_this
memungkinkan Anda untuk bekerja dengan API yang secara khusus menerimashared_ptr<>
. Menurut pendapat saya, API seperti itu biasanya Doing It Wrong (karena lebih baik membiarkan sesuatu yang lebih tinggi di stack memiliki memori) tetapi jika Anda dipaksa untuk bekerja dengan API seperti itu, ini adalah pilihan yang baik.Ini persis sama di c ++ 11 dan yang lebih baru: Ini untuk mengaktifkan kemampuan untuk kembali
this
sebagai pointer bersama karenathis
memberi Anda pointer mentah.dengan kata lain, ini memungkinkan Anda untuk mengubah kode seperti ini
dalam hal ini:
sumber
shared_ptr
. Anda mungkin ingin mengubah antarmuka untuk memastikan itu terjadi.std::shared_ptr<Node> getParent const()
, saya biasanya akan mengeksposnya sebagaiNodePtr getParent const()
gantinya. Jika Anda benar-benar membutuhkan akses ke pointer mentah internal (contoh terbaik: berurusan dengan perpustakaan C), adastd::shared_ptr<T>::get
untuk itu, yang saya benci sebutkan karena saya sudah accessor pointer mentah ini digunakan terlalu banyak kali untuk alasan yang salah.Cara lain adalah menambahkan
weak_ptr<Y> m_stub
anggota ke dalamclass Y
. Lalu menulis:Berguna saat Anda tidak dapat mengubah kelas tempat Anda berasal (misalnya memperluas perpustakaan orang lain). Jangan lupa untuk menginisialisasi anggota, misalnya dengan
m_stub = shared_ptr<Y>(this)
, ini berlaku bahkan selama konstruktor.Tidak apa-apa jika ada lebih banyak bertopik seperti ini dalam hierarki warisan, itu tidak akan mencegah penghancuran objek.
Sunting: Seperti yang ditunjukkan dengan benar oleh pengguna nobar, kode akan menghancurkan objek Y ketika tugas selesai dan variabel sementara dihancurkan. Karena itu jawaban saya salah.
sumber
shared_ptr<>
yang tidak menghapus poinnya, ini berlebihan. Anda bisa mengatakan direturn shared_ptr<Y>(this, no_op_deleter);
manano_op_deleter
objek fungsi unary mengambilY*
dan tidak melakukan apa pun.m_stub = shared_ptr<Y>(this)
akan membuat dan segera merusak shared_ptr sementara dari ini. Ketika pernyataan ini selesai,this
akan dihapus dan semua referensi selanjutnya akan menjuntai.enable_shared_from_this
, itu membuat ituweak_ptr
sendiri (diisi oleh ctor), dikembalikan sebagaishared_ptr
saat Anda meneleponshared_from_this
. Dengan kata lain, Anda menduplikasi apa yangenable_shared_from_this
sudah disediakan.