Jawaban:
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 dari std::enable_shared_from_this
. Saya tidak tahu apakah semantik Boost diperbarui untuk mendukung ini.
std::shared_ptr
untuk objek yang sudah dikelola oleh orang lain std::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:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
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:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
Kode ini memiliki masalah yang sama dengan contoh sebelumnya, meskipun dalam bentuk yang lebih halus. Ketika dibangun, shared_pt
objek r sp1
memiliki sumber daya yang baru dialokasikan. Kode di dalam fungsi anggota S::dangerous
tidak tahu tentang shared_ptr
objek itu, jadi shared_ptr
objek yang dikembalikannya berbeda sp1
. Menyalin shared_ptr
objek baru ke sp2
tidak membantu; ketika sp2
keluar dari ruang lingkup, itu akan melepaskan sumber daya, dan ketika sp1
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:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
Ketika Anda melakukan ini, perlu diingat bahwa objek yang Anda panggil shared_from_this
harus dimiliki oleh suatu shared_ptr
objek. Ini tidak akan berfungsi:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
shared_ptr<S> sp1(new S);
lebih disukai untuk digunakan shared_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 menelepon shared_from_this()
pertama kali! Ini sangat mudah salah! Sebelum C ++ 17, ini adalah UB untuk memanggil shared_from_this()
sebelum tepat satu shared_ptr telah dibuat dengan cara biasa: auto sptr = std::make_shared<S>();
atau shared_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 dipanggil always_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>
menambahkan weak_ptr<T>
contoh pribadi T
yang memegang ' satu hitungan referensi yang benar ' untuk contoh T
.
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 baru shared_ptr
pada dasarnya mendukung ini weak_ptr
.
T
kemudian dapat, dalam metodenya, panggilan shared_from_this
untuk mendapatkan sebuah instance dari shared_ptr<T>
yang mendukung jumlah referensi yang disimpan secara internal yang sama . Dengan cara ini, Anda selalu memiliki satu tempat menyimpan T*
penghitungan ulang alih-alih memiliki banyak shared_ptr
contoh yang tidak saling mengenal, dan masing-masing menganggap mereka shared_ptr
yang bertanggung jawab menghitung ulang T
dan menghapusnya saat ref -hitung mencapai nol.
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 memanggil shared_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 mendapatkan shared_ptr<T>
dari T*
, tetapi pada kenyataannya ketika Anda mendapatkan pointer T* 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.
enable_shared_from_this
memungkinkan Anda untuk bekerja dengan API yang secara khusus menerima shared_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 karena this
memberi Anda pointer mentah.
dengan kata lain, ini memungkinkan Anda untuk mengubah kode seperti ini
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
dalam hal ini:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
shared_ptr
. Anda mungkin ingin mengubah antarmuka untuk memastikan itu terjadi.
std::shared_ptr<Node> getParent const()
, saya biasanya akan mengeksposnya sebagai NodePtr getParent const()
gantinya. Jika Anda benar-benar membutuhkan akses ke pointer mentah internal (contoh terbaik: berurusan dengan perpustakaan C), ada std::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 dalam class Y
. Lalu menulis:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
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.
shared_ptr<>
yang tidak menghapus poinnya, ini berlebihan. Anda bisa mengatakan di return shared_ptr<Y>(this, no_op_deleter);
mana no_op_deleter
objek fungsi unary mengambil Y*
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 itu weak_ptr
sendiri (diisi oleh ctor), dikembalikan sebagai shared_ptr
saat Anda menelepon shared_from_this
. Dengan kata lain, Anda menduplikasi apa yang enable_shared_from_this
sudah disediakan.