Saya mulai mempelajari smart pointer C ++ 11 dan saya tidak melihat adanya manfaat penggunaan std::weak_ptr
. Adakah yang bisa memberi tahu saya kapan std::weak_ptr
bermanfaat?
c++11
shared-ptr
weak-ptr
c++-faq
artm
sumber
sumber
Jawaban:
Contoh yang baik adalah cache.
Untuk objek yang baru-baru ini diakses, Anda ingin menyimpannya dalam memori, jadi Anda memegang pointer kuat ke sana. Secara berkala, Anda memindai cache dan memutuskan objek mana yang belum diakses baru-baru ini. Anda tidak perlu menyimpannya di memori, jadi Anda menyingkirkan pointer kuat.
Tetapi bagaimana jika objek itu sedang digunakan dan beberapa kode lain memegang pointer kuat untuk itu? Jika cache menyingkirkan satu-satunya penunjuk ke objek, ia tidak akan pernah menemukannya lagi. Jadi, cache menyimpan pointer yang lemah ke objek-objek yang perlu ditemukannya jika mereka tetap tersimpan di memori.
Inilah yang dilakukan oleh pointer lemah - ini memungkinkan Anda untuk menemukan objek jika masih ada, tetapi tidak menyimpannya jika tidak ada yang membutuhkannya.
sumber
std::weak_ptr
adalah cara yang sangat baik untuk memecahkan masalah penunjuk menggantung . Dengan hanya menggunakan pointer mentah, tidak mungkin untuk mengetahui apakah data yang direferensikan telah dialokasikan atau tidak. Sebagai gantinya, dengan membiarkanstd::shared_ptr
mengelola data, dan memasokstd::weak_ptr
ke pengguna data, pengguna dapat memeriksa validitas data dengan meneleponexpired()
ataulock()
.Anda tidak dapat melakukan ini
std::shared_ptr
sendirian, karena semuastd::shared_ptr
instance berbagi kepemilikan data yang tidak dihapus sebelum semua instancestd::shared_ptr
dihapus. Berikut ini adalah contoh cara memeriksa penunjuk menggantung menggunakanlock()
:sumber
std::weak_ptr::lock
membuat barustd::shared_ptr
yang berbagi kepemilikan dari objek yang dikelola.Jawaban lain, semoga lebih sederhana. (untuk sesama googler)
Misalkan Anda punya
Team
danMember
benda.Jelas itu hubungan:
Team
objek akan memiliki petunjuk untuk ituMembers
. Dan kemungkinan para anggota juga akan memiliki pointer belakang keTeam
objek mereka .Maka Anda memiliki siklus ketergantungan. Jika Anda menggunakan
shared_ptr
, objek tidak akan lagi secara otomatis dibebaskan ketika Anda meninggalkan referensi pada mereka, karena mereka referensi satu sama lain secara siklik. Ini adalah kebocoran memori.Anda memecahkan ini dengan menggunakan
weak_ptr
. "Pemilik" biasanya menggunakanshared_ptr
dan "dimiliki" menggunakanweak_ptr
untuk induknya, dan mengubahnya sementara untukshared_ptr
saat dibutuhkan akses ke induknya.Simpan ptr lemah:
lalu gunakan saat dibutuhkan
sumber
shared_ptr
adalah untuk berbagi kepemilikan, jadi tidak ada yang memiliki tanggung jawab khusus untuk membebaskan memori, itu dibebaskan secara otomatis ketika tidak lagi digunakan. Kecuali jika ada loop ... Anda mungkin memiliki beberapa tim berbagi pemain yang sama (tim yang lalu?). Jika objek tim "memiliki" anggota, maka tidak perlu menggunakan ashared_ptr
untuk memulai.shared_ptr
dirujuk oleh "anggota tim", kapan akan dihancurkan? Apa yang Anda gambarkan adalah kasus di mana tidak ada loop.Inilah salah satu contoh, yang diberikan kepada saya oleh @jleahy: Misalkan Anda memiliki koleksi tugas, dijalankan secara tidak sinkron, dan dikelola oleh
std::shared_ptr<Task>
. Anda mungkin ingin melakukan sesuatu dengan tugas-tugas itu secara berkala, sehingga acara pengatur waktu dapat melintasistd::vector<std::weak_ptr<Task>>
dan memberikan tugas-tugas itu sesuatu untuk dilakukan. Namun, secara bersamaan suatu tugas mungkin secara bersamaan memutuskan bahwa itu tidak lagi diperlukan dan mati. Timer sehingga dapat memeriksa apakah tugas itu masih hidup dengan membuat pointer bersama dari pointer lemah dan menggunakan pointer bersama, asalkan bukan nol.sumber
Mereka berguna dengan Boost.Asio ketika Anda tidak dijamin bahwa objek target masih ada ketika penangan asinkron dipanggil. Caranya adalah dengan mengikat sebuah
weak_ptr
ke objek handler asynchonous, menggunakanstd::bind
atau lambda menangkap.Ini adalah varian dari
self = shared_from_this()
idiom yang sering terlihat dalam Boost. Contoh lain, di mana penangan asinkron yang tertunda tidak akan memperpanjang masa pakai objek target, namun tetap aman jika objek target dihapus.sumber
this
self = shared_from_this()
idiom ketika pawang memanggil metode dalam kelas yang sama.shared_ptr : memegang objek nyata.
kelemahan_ptr : digunakan
lock
untuk terhubung ke pemilik asli atau mengembalikan NULLshared_ptr
jika tidak.Secara kasar,
weak_ptr
perannya mirip dengan peran agen perumahan . Tanpa agen, untuk mendapatkan rumah sewaan kita mungkin harus memeriksa rumah acak di kota. Agen memastikan bahwa kami mengunjungi hanya rumah-rumah yang masih dapat diakses dan tersedia untuk disewa.sumber
weak_ptr
juga baik untuk memeriksa penghapusan objek yang benar - terutama dalam tes unit. Kasus penggunaan umum mungkin terlihat seperti ini:sumber
Ketika menggunakan pointer, penting untuk memahami berbagai jenis pointer yang tersedia dan kapan menggunakan masing-masing pointer. Ada empat jenis pointer dalam dua kategori sebagai berikut:
SomeClass* ptrToSomeClass = new SomeClass();
]std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() );
]
std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );
]
std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr );
]
Pointer mentah (kadang-kadang disebut sebagai "legacy pointer", atau "pointer C") memberikan perilaku pointer 'telanjang-tulang' dan merupakan sumber bug dan kebocoran memori yang umum. Pointer mentah tidak menyediakan sarana untuk melacak kepemilikan sumber daya dan pengembang harus memanggil 'delete' secara manual untuk memastikan mereka tidak membuat kebocoran memori. Ini menjadi sulit jika sumber daya dibagikan karena dapat menjadi tantangan untuk mengetahui apakah ada benda yang masih menunjuk ke sumber daya. Karena alasan ini, pointer mentah umumnya harus dihindari dan hanya digunakan di bagian kritis-kinerja dari kode dengan cakupan terbatas.
Pointer unik adalah pointer cerdas dasar yang 'memiliki' pointer mentah yang mendasari ke sumber daya dan bertanggung jawab untuk memanggil hapus dan membebaskan memori yang dialokasikan setelah objek yang 'memiliki' pointer unik keluar dari ruang lingkup. Nama 'unik' merujuk pada fakta bahwa hanya satu objek yang dapat 'memiliki' pointer unik pada titik waktu tertentu. Kepemilikan dapat ditransfer ke objek lain melalui perintah move, tetapi pointer unik tidak pernah dapat disalin atau dibagikan. Untuk alasan ini, pointer unik adalah alternatif yang baik untuk pointer mentah jika hanya ada satu objek yang membutuhkan pointer pada waktu tertentu, dan ini mengurangi pengembang dari kebutuhan untuk membebaskan memori pada akhir siklus hidup objek yang dimiliki.
Pointer bersama adalah tipe lain dari pointer cerdas yang mirip dengan pointer unik, tetapi memungkinkan banyak objek memiliki kepemilikan atas pointer bersama. Seperti pointer unik, pointer bersama bertanggung jawab untuk membebaskan memori yang dialokasikan setelah semua objek selesai menunjuk ke sumber daya. Ini menyelesaikan ini dengan teknik yang disebut penghitungan referensi. Setiap kali objek baru mengambil kepemilikan dari pointer bersama, jumlah referensi bertambah satu. Demikian pula, ketika suatu objek keluar dari ruang lingkup atau berhenti menunjuk ke sumber daya, jumlah referensi dikurangi oleh satu. Ketika jumlah referensi mencapai nol, memori yang dialokasikan akan dibebaskan. Untuk alasan ini, pointer bersama adalah jenis pointer pintar yang sangat kuat yang harus digunakan kapan saja beberapa objek perlu menunjuk ke sumber daya yang sama.
Akhirnya, pointer lemah adalah tipe lain dari pointer cerdas yang, alih-alih menunjuk ke sumber daya secara langsung, mereka menunjuk ke pointer lain (lemah atau dibagi). Pointer yang lemah tidak dapat mengakses objek secara langsung, tetapi mereka dapat mengetahui apakah objek tersebut masih ada atau jika telah kedaluwarsa. Pointer yang lemah dapat sementara dikonversi menjadi pointer bersama untuk mengakses objek runcing-ke (asalkan masih ada). Untuk menggambarkan, pertimbangkan contoh berikut:
Dalam contoh, Anda memiliki pointer lemah ke Rapat B. Anda bukan "pemilik" di Rapat B sehingga dapat berakhir tanpa Anda, dan Anda tidak tahu apakah itu berakhir atau tidak kecuali Anda memeriksa. Jika belum berakhir, Anda dapat bergabung dan berpartisipasi, jika tidak, Anda tidak bisa. Ini berbeda dari memiliki penunjuk bersama untuk Rapat B karena Anda kemudian akan menjadi "pemilik" di Rapat A dan Rapat B (berpartisipasi di keduanya pada saat yang sama).
Contoh tersebut menggambarkan bagaimana pointer lemah bekerja dan berguna ketika suatu objek perlu menjadi pengamat luar , tetapi tidak ingin tanggung jawab berbagi kepemilikan. Ini sangat berguna dalam skenario bahwa dua objek harus saling menunjuk (alias referensi melingkar). Dengan pointer bersama, tidak ada objek yang dapat dilepaskan karena mereka masih 'sangat' ditunjukkan oleh objek lain. Ketika salah satu pointer adalah pointer lemah, objek yang memegang pointer lemah masih dapat mengakses objek lain saat dibutuhkan, asalkan masih ada.
sumber
Terlepas dari kasus penggunaan yang valid yang telah disebutkan lainnya
std::weak_ptr
adalah alat yang luar biasa dalam lingkungan multithreaded, karenastd::shared_ptr
dalam hubungannya denganstd::weak_ptr
aman terhadap pointer menggantung - berlawanan denganstd::unique_ptr
dalam hubungannya dengan pointer mentahstd::weak_ptr::lock()
adalah operasi atom (lihat juga Tentang keamanan utas dari lemah_ptr )Pertimbangkan tugas untuk memuat semua gambar direktori (~ 10.000) secara bersamaan ke dalam memori (mis. Sebagai cache thumbnail). Jelas cara terbaik untuk melakukan ini adalah thread kontrol, yang menangani dan mengelola gambar, dan beberapa thread pekerja, yang memuat gambar. Sekarang ini adalah tugas yang mudah. Berikut ini adalah implementasi yang sangat disederhanakan (
join()
dll dihilangkan, utas harus ditangani secara berbeda dalam implementasi nyata dll)Tetapi itu menjadi jauh lebih rumit, jika Anda ingin mengganggu pemuatan gambar, misalnya karena pengguna telah memilih direktori yang berbeda. Atau bahkan jika Anda ingin menghancurkan manajer.
Anda perlu komunikasi utas dan harus menghentikan semua utas loader, sebelum Anda dapat mengubah
m_imageDatas
bidang Anda . Kalau tidak, loader akan melanjutkan memuat sampai semua gambar selesai - bahkan jika mereka sudah usang. Dalam contoh sederhana, itu tidak akan terlalu sulit, tetapi dalam lingkungan nyata hal-hal bisa menjadi jauh lebih rumit.Utas mungkin akan menjadi bagian dari rangkaian utas yang digunakan oleh beberapa manajer, di mana sebagian dihentikan, dan sebagian tidak dll. Parameter sederhana
imagesToLoad
akan berupa antrian terkunci, di mana manajer tersebut mendorong permintaan gambar mereka dari utas kontrol yang berbeda dengan pembaca membuka permintaan - dalam urutan acak - di ujung lainnya. Sehingga komunikasi menjadi sulit, lambat dan rawan kesalahan. Cara yang sangat elegan untuk menghindari komunikasi tambahan dalam kasus tersebut adalah dengan menggunakannyastd::shared_ptr
bersamastd::weak_ptr
.Implementasi ini hampir semudah yang pertama, tidak memerlukan komunikasi utas tambahan, dan dapat menjadi bagian dari kumpulan utas / antrian dalam implementasi nyata. Karena gambar yang kedaluwarsa dilewati, dan gambar yang tidak kedaluwarsa diproses, utas tidak akan pernah harus dihentikan selama operasi normal. Anda selalu dapat dengan aman mengubah jalur atau menghancurkan pengelola Anda, karena pembaca dapat memeriksa, jika penunjuk kepemilikan tidak kedaluwarsa.
sumber
http://en.cppreference.com/w/cpp/memory/weak_ptr std :: lemah_ptr adalah penunjuk pintar yang memegang referensi yang tidak memiliki ("lemah") ke objek yang dikelola oleh std :: shared_ptr. Itu harus dikonversi ke std :: shared_ptr untuk mengakses objek yang dirujuk.
std :: lemah_ptr memodelkan kepemilikan sementara: ketika suatu objek hanya perlu diakses jika ada, dan itu dapat dihapus kapan saja oleh orang lain, std :: lemah_ptr digunakan untuk melacak objek, dan itu dikonversi ke std: : shared_ptr untuk mengambil alih kepemilikan sementara. Jika std :: shared_ptr asli dihancurkan saat ini, masa hidup objek akan diperpanjang hingga std sementara: shared_ptr dihancurkan juga.
Selain itu, std :: lemah_ptr digunakan untuk memecah referensi melingkar std :: shared_ptr.
sumber
Ada kekurangan dari pointer bersama: shared_pointer tidak bisa menangani ketergantungan siklus orangtua-anak. Berarti jika kelas induk menggunakan objek kelas anak menggunakan pointer bersama, dalam file yang sama jika kelas anak menggunakan objek dari kelas induk. Pointer bersama akan gagal menghancurkan semua objek, bahkan pointer bersama sama sekali tidak memanggil destruktor dalam skenario ketergantungan siklus. pada dasarnya pointer bersama tidak mendukung mekanisme jumlah referensi.
Kekurangan ini bisa kita atasi menggunakan kelemahan_pointer.
sumber
weak_ptr
kesepakatan dengan ketergantungan sirkuler tanpa perubahan logika program sebagai pengganti drop-inshared_ptr
?" :-)Ketika kami tidak ingin memiliki objek:
Ex:
Di kelas di atas, wPtr1 tidak memiliki sumber daya yang ditunjukkan oleh wPtr1. Jika sumber daya terhapus maka wPtr1 kedaluwarsa.
Untuk menghindari ketergantungan sirkular:
Sekarang jika kita membuat shared_ptr dari kelas B dan A, use_count dari kedua pointer adalah dua.
Ketika shared_ptr keluar lingkup od hitungan masih tetap 1 dan karenanya objek A dan B tidak akan dihapus.
keluaran:
Seperti yang dapat kita lihat dari output bahwa pointer A dan B tidak pernah dihapus dan karenanya kehabisan memori.
Untuk menghindari masalah seperti itu, gunakan lemah_ptr di kelas A alih-alih shared_ptr yang lebih masuk akal.
sumber
Saya melihat
std::weak_ptr<T>
sebagai pegangan untukstd::shared_ptr<T>
: Ini memungkinkan saya untuk mendapatkanstd::shared_ptr<T>
jika masih ada, tetapi tidak akan memperpanjang masa pakainya. Ada beberapa skenario saat sudut pandang seperti itu berguna:Skenario penting lainnya adalah memutus siklus dalam struktur data.
Herb Sutter memiliki ceramah luar biasa yang menjelaskan penggunaan fitur bahasa terbaik (dalam hal ini smart pointer) untuk memastikan Kebocoran Kebocoran secara Default (artinya: semuanya mengklik di tempat dengan konstruksi; Anda tidak dapat mengacaukannya). Ini harus diwaspadai.
sumber