Perbandingan, Pro, Kontra, dan Kapan Menggunakan?
Ini adalah spin-off dari utas pengumpulan sampah di mana apa yang saya anggap sebagai jawaban sederhana menghasilkan banyak komentar tentang beberapa implementasi penunjuk cerdas tertentu sehingga sepertinya layak untuk memulai posting baru.
Pada akhirnya, pertanyaannya adalah apa saja berbagai implementasi smart pointer di C ++ di luar sana dan bagaimana cara membandingkannya? Hanya pro dan kontra sederhana atau pengecualian dan mendapatkan sesuatu yang menurut Anda seharusnya berhasil.
Saya telah memposting beberapa implementasi yang telah saya gunakan atau setidaknya abaikan dan pertimbangkan untuk digunakan sebagai jawaban di bawah ini dan pemahaman saya tentang perbedaan dan persamaan mereka yang mungkin tidak 100% akurat jadi silakan periksa fakta atau koreksi saya sesuai kebutuhan.
Tujuannya adalah untuk mempelajari tentang beberapa objek dan pustaka baru atau memperbaiki penggunaan dan pemahaman saya tentang implementasi yang ada yang sudah banyak digunakan dan berakhir dengan referensi yang layak untuk orang lain.
sumber
osg::ref_ptr
.std::auto_ptr
.std::auto_ptr_ref
adalah detail desainstd::auto_ptr
.std::auto_ptr
tidak ada hubungannya dengan pengumpulan sampah, tujuan utamanya adalah secara khusus untuk memungkinkan pengecualian transfer kepemilikan yang aman, terutama dalam situasi pemanggilan fungsi dan pengembalian fungsi.std::unique_ptr
hanya dapat memecahkan "masalah" yang Anda kutip dengan penampung standar karena C ++ telah berubah untuk memungkinkan perbedaan antara pemindahan dan penyalinan dan penampung standar telah berubah untuk memanfaatkan hal ini.auto_ptr_ref
menjadi detail implementasi). Namun, saya setuju bahwa Anda harus memposting ini sebagai jawaban dan merumuskan ulang pertanyaan menjadi pertanyaan yang sebenarnya. Ini kemudian dapat berfungsi sebagai referensi di masa mendatang.Jawaban:
C ++ 03
std::auto_ptr
- Mungkin salah satu yang asli menderita sindroma draf pertama hanya karena fasilitas pengumpulan sampah terbatas. Kelemahan pertama adalah bahwa ia memanggildelete
pemusnahan membuat mereka tidak dapat diterima untuk menampung array yang dialokasikan objek (new[]
). Ini mengambil kepemilikan penunjuk sehingga dua penunjuk otomatis tidak boleh berisi objek yang sama. Tugas akan mentransfer kepemilikan dan mengatur ulang penunjuk otomatis rvalue ke penunjuk nol. Yang mungkin mengarah pada kelemahan terburuk; mereka tidak dapat digunakan dalam kontainer STL karena ketidakmampuan untuk disalin. Pukulan terakhir untuk setiap kasus penggunaan adalah mereka dijadwalkan untuk tidak digunakan lagi dalam standar C ++ berikutnya.std::auto_ptr_ref
- Ini bukan penunjuk cerdas, ini sebenarnya adalah detail desain yang digunakanstd::auto_ptr
untuk memungkinkan penyalinan dan penugasan dalam situasi tertentu. Secara khusus ini dapat digunakan untuk mengubah non-conststd::auto_ptr
menjadi nilai l menggunakan trik Colvin-Gibbons yang juga dikenal sebagai konstruktor pemindahan untuk mentransfer kepemilikan.Sebaliknya, mungkin
std::auto_ptr
tidak benar-benar dimaksudkan untuk digunakan sebagai penunjuk cerdas tujuan umum untuk pengumpulan sampah otomatis. Sebagian besar pemahaman dan asumsi saya yang terbatas didasarkan pada Penggunaan Efektif Herb Sutter dari auto_ptr dan saya menggunakannya secara teratur meskipun tidak selalu dengan cara yang paling optimal.C ++ 11
std::unique_ptr
- Ini adalah teman kita yang akan menggantinyastd::auto_ptr
akan sangat mirip kecuali dengan perbaikan utama untuk memperbaiki kelemahanstd::auto_ptr
seperti bekerja dengan array, perlindungan lvalue melalui konstruktor salinan pribadi, dapat digunakan dengan kontainer dan algoritma STL, dll. Karena itu overhead kinerja dan jejak memori terbatas, ini adalah kandidat yang ideal untuk menggantikan, atau mungkin lebih tepat digambarkan sebagai memiliki, petunjuk mentah. Karena "unik" menyiratkan hanya ada satu pemilik penunjuk seperti sebelumnyastd::auto_ptr
.std::shared_ptr
- Saya yakin ini didasarkan pada TR1 danboost::shared_ptr
tetapi ditingkatkan untuk menyertakan aritmatika aliasing dan pointer juga. Singkatnya, ini membungkus referensi pointer pintar terhitung di sekitar objek yang dialokasikan secara dinamis. Karena "bersama" menyiratkan bahwa penunjuk dapat dimiliki oleh lebih dari satu penunjuk bersama ketika referensi terakhir dari penunjuk bersama terakhir keluar dari ruang lingkup maka objek akan dihapus dengan tepat. Ini juga aman untuk benang dan dapat menangani jenis yang tidak lengkap dalam banyak kasus.std::make_shared
dapat digunakan untuk membuatstd::shared_ptr
alokasi heap dengan satu heap secara efisien menggunakan pengalokasi default.std::weak_ptr
- Juga berdasarkan TR1 danboost::weak_ptr
. Ini adalah referensi ke objek yang dimiliki oleh astd::shared_ptr
dan oleh karena itu tidak akan mencegah penghapusan objek jika jumlahstd::shared_ptr
referensi turun ke nol. Untuk mendapatkan akses ke pointer mentah, pertama-tama Anda harus mengaksesstd::shared_ptr
by callinglock
yang akan mengembalikan kosongstd::shared_ptr
jika pointer yang dimiliki telah kedaluwarsa dan sudah dimusnahkan. Ini terutama berguna untuk menghindari penghitungan referensi gantung yang tidak terbatas saat menggunakan beberapa petunjuk cerdas.Dorongan
boost::shared_ptr
- Mungkin yang paling mudah digunakan dalam skenario yang paling bervariasi (STL, PIMPL, RAII, dll) ini adalah penunjuk cerdas terhitung yang dirujuk bersama. Saya telah mendengar beberapa keluhan tentang kinerja dan overhead dalam beberapa situasi tetapi saya pasti mengabaikannya karena saya tidak dapat mengingat apa argumennya. Rupanya itu cukup populer untuk menjadi objek C ++ standar yang tertunda dan tidak ada kelemahan atas norma mengenai petunjuk cerdas yang muncul di benak.boost::weak_ptr
- Sama seperti deskripsi sebelumnya tentangstd::weak_ptr
, berdasarkan implementasi ini, ini memungkinkan referensi yang tidak memiliki referensi untuk aboost::shared_ptr
. Anda tidak mengherankan memanggillock()
untuk mengakses penunjuk bersama "kuat" dan harus memeriksa untuk memastikan itu valid karena bisa saja sudah dimusnahkan. Pastikan untuk tidak menyimpan penunjuk bersama yang dikembalikan dan membiarkannya keluar dari ruang lingkup segera setelah Anda selesai dengannya jika tidak, Anda akan langsung kembali ke masalah referensi siklik di mana jumlah referensi Anda akan hang dan objek tidak akan dimusnahkan.boost::scoped_ptr
- Ini adalah kelas penunjuk cerdas sederhana dengan sedikit overhead yang mungkin dirancang untuk alternatif yang berkinerja lebih baik daripadaboost::shared_ptr
saat dapat digunakan. Ini sebanding denganstd::auto_ptr
fakta bahwa itu tidak dapat digunakan dengan aman sebagai elemen dari wadah STL atau dengan banyak penunjuk ke objek yang sama.boost::intrusive_ptr
- Saya belum pernah menggunakan ini, tetapi dari pemahaman saya, ini dirancang untuk digunakan saat membuat kelas yang kompatibel dengan penunjuk cerdas Anda sendiri. Anda perlu menerapkan penghitungan referensi sendiri, Anda juga perlu menerapkan beberapa metode jika Anda ingin kelas Anda menjadi generik, selanjutnya Anda harus menerapkan keamanan utas Anda sendiri. Sisi positifnya, ini mungkin memberi Anda cara yang paling sesuai untuk memilih dan memilih dengan tepat seberapa banyak atau sedikit "kecerdasan" yang Anda inginkan.intrusive_ptr
biasanya lebih efisien daripadashared_ptr
karena memungkinkan Anda memiliki satu alokasi heap per objek. (terima kasih Arvid)boost::shared_array
- Iniboost::shared_ptr
untuk array. Pada dasarnyanew []
,,operator[]
dan tentu sajadelete []
dipanggang. Ini dapat digunakan dalam wadah STL dan sejauh yang saya tahu semuanya dapatboost:shared_ptr
dilakukan meskipun Anda tidak dapat menggunakannyaboost::weak_ptr
dengan ini. Namun, Anda dapat menggunakan aboost::shared_ptr<std::vector<>>
untuk fungsionalitas serupa dan mendapatkan kembali kemampuan untuk menggunakanboost::weak_ptr
referensi.boost::scoped_array
- Iniboost::scoped_ptr
untuk array. Seperti denganboost::shared_array
semua kebaikan array yang diperlukan telah dimasukkan. Yang ini tidak dapat disalin sehingga tidak dapat digunakan dalam kontainer STL. Saya telah menemukan hampir di mana pun Anda ingin menggunakan ini, Anda mungkin bisa menggunakannyastd::vector
. Saya tidak pernah menentukan mana yang sebenarnya lebih cepat atau memiliki overhead yang lebih sedikit tetapi array cakupan ini tampaknya jauh lebih tidak terlibat daripada vektor STL. Saat Anda ingin menyimpan alokasi di tumpukan, pertimbangkanboost::array
sebagai gantinya.Qt
QPointer
- Diperkenalkan di Qt 4.0, ini adalah penunjuk pintar "lemah" yang hanya bekerja denganQObject
dan kelas turunan, yang dalam kerangka kerja Qt hampir semuanya jadi itu sebenarnya bukan batasan. Namun ada batasan yaitu tidak menyediakan pointer yang "kuat" dan meskipun Anda dapat memeriksa apakah objek yang mendasari valid denganisNull()
Anda dapat menemukan objek Anda sedang dimusnahkan tepat setelah Anda melewati pemeriksaan itu terutama di lingkungan multi-threaded. Qt orang menganggap ini usang, saya percaya.QSharedDataPointer
- Ini adalah penunjuk cerdas "kuat" yang berpotensi sebanding denganboost::intrusive_ptr
meskipun memiliki beberapa keamanan utas bawaan tetapi memerlukan Anda untuk menyertakan metode penghitungan referensi (ref
danderef
) yang dapat Anda lakukan dengan membuat subkelasQSharedData
. Seperti kebanyakan Qt, objek paling baik digunakan melalui pewarisan yang cukup dan subclassing semuanya tampaknya menjadi desain yang dimaksudkan.QExplicitlySharedDataPointer
- Sangat mirip denganQSharedDataPointer
kecuali itu tidak secara implisit memanggildetach()
. Saya akan menyebut versi 2.0 iniQSharedDataPointer
karena sedikit peningkatan kontrol tentang kapan tepatnya harus melepaskan setelah jumlah referensi turun ke nol tidak terlalu berharga untuk objek yang sama sekali baru.QSharedPointer
- Penghitungan referensi atom, thread safe, pointer yang dapat dibagikan, penghapusan kustom (dukungan array), terdengar seperti segala sesuatu yang seharusnya menjadi pointer cerdas. Inilah yang terutama saya gunakan sebagai penunjuk pintar di Qt dan saya merasa sebanding denganboost:shared_ptr
meskipun mungkin secara signifikan lebih banyak overhead seperti banyak objek di Qt.QWeakPointer
- Apakah Anda merasakan pola yang berulang? Sama sepertistd::weak_ptr
danboost::weak_ptr
ini digunakan dalam hubungannya denganQSharedPointer
saat Anda membutuhkan referensi antara dua penunjuk cerdas yang sebaliknya akan menyebabkan objek Anda tidak akan pernah dihapus.QScopedPointer
- Nama ini juga harus terlihat akrab dan sebenarnya didasarkan padaboost::scoped_ptr
tidak seperti versi Qt dari petunjuk bersama dan lemah. Ini berfungsi untuk menyediakan penunjuk cerdas pemilik tunggal tanpa overheadQSharedPointer
yang membuatnya lebih cocok untuk kompatibilitas, kecuali kode aman, dan semua hal yang mungkin Anda gunakanstd::auto_ptr
atauboost::scoped_ptr
untuk.sumber
intrusive_ptr
biasanya lebih efisien daripadashared_ptr
, karena ini memungkinkan Anda memiliki alokasi heap tunggal per objek.shared_ptr
dalam kasus umum akan mengalokasikan objek heap kecil yang terpisah untuk penghitung referensi.std::make_shared
dapat digunakan untuk mendapatkan yang terbaik dari kedua dunia.shared_ptr
dengan hanya satu alokasi heap.shared_ptr
s? (Tidak termasuk menyelesaikan referensi siklik)Ada juga Loki yang mengimplementasikan smart pointers berbasis kebijakan.
Referensi lain tentang petunjuk cerdas berbasis kebijakan, menangani masalah buruknya dukungan pengoptimalan basis kosong bersama dengan beberapa warisan oleh banyak penyusun:
sumber
Selain yang diberikan, ada juga beberapa yang berorientasi pada keselamatan:
SaferCPlusPlus
mse::TRefCountingPointer
adalah referensi menghitung penunjuk pintar sepertistd::shared_ptr
. Perbedaannyamse::TRefCountingPointer
adalah lebih aman, lebih kecil dan lebih cepat, tetapi tidak memiliki mekanisme pengaman benang. Dan itu datang dalam versi "not null" dan "fixed" (non-retargetable) yang dapat diasumsikan dengan aman untuk selalu mengarah ke objek yang dialokasikan secara valid. Jadi pada dasarnya, jika objek target Anda dibagikan di antara utas asinkronstd::shared_ptr
, gunakan , jika tidakmse::TRefCountingPointer
akan lebih optimal.mse::TScopeOwnerPointer
mirip denganboost::scoped_ptr
, tetapi bekerja bersama denganmse::TScopeFixedPointer
dalam hubungan penunjuk "kuat-lemah" sepertistd::shared_ptr
danstd::weak_ptr
.mse::TScopeFixedPointer
menunjuk ke objek yang dialokasikan di tumpukan, atau yang penunjuk "miliknya" dialokasikan di tumpukan. Ini (sengaja) dibatasi dalam fungsinya untuk meningkatkan keamanan waktu kompilasi tanpa biaya runtime. Inti dari pointer "cakupan" pada dasarnya adalah untuk mengidentifikasi serangkaian keadaan yang sederhana dan cukup deterministik sehingga tidak diperlukan mekanisme keselamatan (runtime).mse::TRegisteredPointer
berperilaku seperti pointer mentah, kecuali nilainya secara otomatis disetel ke null_ptr saat objek target dihancurkan. Ini dapat digunakan sebagai pengganti umum untuk petunjuk mentah dalam banyak situasi. Seperti pointer mentah, itu tidak memiliki keamanan benang intrinsik. Namun sebagai gantinya, tidak ada masalah menargetkan objek yang dialokasikan di stack (dan mendapatkan manfaat kinerja yang sesuai). Ketika pemeriksaan run-time diaktifkan, penunjuk ini aman dari mengakses memori yang tidak valid. Karenamse::TRegisteredPointer
memiliki perilaku yang sama dengan penunjuk mentah saat menunjuk ke objek yang valid, ia dapat "dinonaktifkan" (secara otomatis diganti dengan penunjuk mentah yang sesuai) dengan arahan waktu kompilasi, yang memungkinkannya digunakan untuk membantu menangkap bug dalam debug / uji / mode beta saat tidak menimbulkan biaya tambahan dalam mode rilis.Berikut adalah artikel yang menjelaskan mengapa dan bagaimana menggunakannya. (Catatan, steker tidak tahu malu.)
sumber