smart pointer (boost) menjelaskan

220

Apa perbedaan antara set pointer berikut? Kapan Anda menggunakan setiap pointer dalam kode produksi, jika sama sekali?

Contohnya akan dihargai!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Apakah Anda menggunakan peningkatan dalam kode produksi?

Batal
sumber

Jawaban:

339

Properti dasar pointer pintar

Mudah ketika Anda memiliki properti yang dapat Anda tetapkan setiap penunjuk pintar. Ada tiga sifat penting.

  • tidak ada kepemilikan sama sekali
  • transfer kepemilikan
  • bagian kepemilikan

Yang pertama berarti bahwa pointer pintar tidak dapat menghapus objek, karena itu bukan miliknya. Yang kedua berarti bahwa hanya satu pointer cerdas yang dapat menunjuk ke objek yang sama pada saat yang sama. Jika smart pointer harus dikembalikan dari fungsi, kepemilikannya ditransfer ke smart pointer yang dikembalikan, misalnya.

Yang ketiga berarti bahwa beberapa pointer cerdas dapat menunjuk ke objek yang sama pada saat yang sama. Ini berlaku untuk pointer mentah juga, tetapi pointer mentah tidak memiliki fitur penting: Mereka tidak menentukan apakah mereka memiliki atau tidak. Bagian dari smart pointer kepemilikan akan menghapus objek jika setiap pemilik menyerahkan objek. Perilaku ini sering dibutuhkan, sehingga shared smart pointer sering digunakan secara luas.

Beberapa orang yang memiliki smart pointer tidak mendukung yang kedua maupun yang ketiga. Karena itu mereka tidak dapat dikembalikan dari fungsi atau dilewatkan di tempat lain. Yang paling cocok untuk RAIItujuan di mana pointer cerdas disimpan lokal dan baru saja dibuat sehingga membebaskan objek setelah keluar dari ruang lingkup.

Pangsa kepemilikan dapat diimplementasikan dengan memiliki copy constructor. Ini secara alami menyalin pointer cerdas dan baik salinan maupun aslinya akan merujuk objek yang sama. Transfer kepemilikan tidak dapat benar-benar diterapkan dalam C ++ saat ini, karena tidak ada cara untuk mentransfer sesuatu dari satu objek ke yang lain yang didukung oleh bahasa: Jika Anda mencoba mengembalikan objek dari suatu fungsi, yang terjadi adalah objek tersebut disalin. Jadi penunjuk cerdas yang mengimplementasikan transfer kepemilikan harus menggunakan copy constructor untuk mengimplementasikan transfer kepemilikan tersebut. Namun, ini pada gilirannya memecah penggunaannya dalam wadah, karena persyaratan menyatakan perilaku tertentu dari copy constructor elemen kontainer yang tidak sesuai dengan perilaku yang disebut "moving constructor" dari smart pointer ini.

C ++ 1x memberikan dukungan asli untuk transfer kepemilikan dengan memperkenalkan apa yang disebut "memindahkan konstruktor" dan "memindahkan operator penugasan". Ini juga dilengkapi dengan smart pointer transfer kepemilikan yang disebut unique_ptr.

Mengkategorikan pointer cerdas

scoped_ptradalah penunjuk cerdas yang tidak dapat ditransfer atau dibagikan. Ini hanya dapat digunakan jika Anda secara lokal perlu mengalokasikan memori, tetapi pastikan itu dibebaskan lagi ketika keluar dari ruang lingkup. Tetapi masih dapat ditukar dengan scoped_ptr lain, jika Anda ingin melakukannya.

shared_ptradalah penunjuk cerdas yang membagi kepemilikan (jenis ketiga di atas). Referensi dihitung sehingga dapat melihat kapan salinan terakhir keluar dari ruang lingkup dan kemudian membebaskan objek yang dikelola.

weak_ptradalah penunjuk cerdas yang tidak memiliki. Ini digunakan untuk mereferensikan objek yang dikelola (dikelola oleh shared_ptr) tanpa menambahkan jumlah referensi. Biasanya, Anda harus mengeluarkan pointer mentah dari shared_ptr dan menyalinnya. Tapi itu tidak akan aman, karena Anda tidak akan memiliki cara untuk memeriksa kapan objek itu benar-benar dihapus. Jadi, lemah_ptr menyediakan sarana dengan mereferensikan objek yang dikelola oleh shared_ptr. Jika Anda perlu mengakses objek, Anda bisa mengunci manajemennya (untuk menghindari itu di utas lain shared_ptr membebaskannya saat Anda menggunakan objek) dan kemudian menggunakannya. Jika lemah_ptr menunjuk ke suatu objek yang sudah dihapus, ia akan melihat Anda dengan melemparkan pengecualian. Menggunakan lemah_ptr paling bermanfaat ketika Anda memiliki referensi siklik: Menghitung referensi tidak dapat dengan mudah mengatasi situasi seperti itu.

intrusive_ptrseperti shared_ptr tetapi tidak menyimpan penghitungan referensi dalam shared_ptr tetapi membiarkan penambahan / pengurangan jumlah ke beberapa fungsi pembantu yang perlu didefinisikan oleh objek yang dikelola. Ini memiliki keuntungan bahwa objek yang sudah direferensikan (yang memiliki jumlah referensi bertambah dengan mekanisme penghitungan referensi eksternal) dapat dimasukkan ke dalam intrusive_ptr - karena jumlah referensi tidak lagi internal ke penunjuk pintar, tetapi penunjuk pintar menggunakan yang sudah ada mekanisme penghitungan referensi.

unique_ptradalah transfer pointer kepemilikan. Anda tidak bisa menyalinnya, tetapi Anda bisa memindahkannya dengan menggunakan konstruktor gerakan C ++ 1x:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

Ini adalah semantik yang std :: auto_ptr taat, tetapi karena kehilangan dukungan asli untuk bergerak, gagal menyediakannya tanpa jebakan. unique_ptr akan secara otomatis mencuri sumber daya dari temporary_ptr lain sementara yang merupakan salah satu fitur kunci semantik bergerak. auto_ptr akan ditinggalkan dalam rilis C ++ Standard berikutnya yang mendukung unique_ptr. C ++ 1x juga akan memungkinkan benda isian yang hanya bergerak tetapi tidak dapat disalin ke dalam wadah. Jadi Anda dapat memasukkan unik_ptr ke vektor misalnya. Saya akan berhenti di sini dan merujuk Anda ke artikel bagus tentang ini jika Anda ingin membaca lebih lanjut tentang ini.

Johannes Schaub - litb
sumber
3
terima kasih atas pujiannya Saya menghargainya sehingga Anda akan mendapatkan +1 sekarang juga: p
Johannes Schaub - litb
@ litb: Saya ragu dalam "transfer kepemilikan"; Saya setuju tidak ada transfer kepemilikan nyata antara objek di C ++ 03, tetapi untuk smart pointer tidak dapat dilakukan, oleh mekanisme copy destruktif yang dinyatakan di sini informit.com/articles/article.aspx?p=31529&seqNum= 5 .
legends2k
3
jawaban yang fantastis. Catatan: auto_ptrsudah usang (C ++ 11).
Nickolay
2
"ini pada gilirannya memutus penggunaannya dalam wadah, karena persyaratan menyatakan perilaku tertentu dari copy constructor elemen kontainer yang tidak sesuai dengan perilaku" moving constructor "yang disebut pointer pintar ini." Tidak mendapatkan bagian itu.
Raja
Saya juga telah diberitahu bahwa intrusive_ptrlebih baik shared_ptruntuk koherensi cache yang lebih baik. Tampaknya cache berkinerja lebih baik jika Anda menyimpan jumlah referensi sebagai bagian dari memori objek yang dikelola itu sendiri, bukan objek yang terpisah. Ini dapat diimplementasikan dalam templat atau superclass dari objek yang dikelola.
Eliot
91

scoped_ptr adalah yang paling sederhana. Ketika keluar dari ruang lingkup, itu dihancurkan. Kode berikut ini ilegal (scoped_ptrs tidak dapat disalin) tetapi akan menggambarkan suatu hal:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr dihitung referensi. Setiap kali salinan atau penugasan terjadi, jumlah referensi bertambah. Setiap kali destruktor instance dipecat, jumlah referensi untuk T * mentah dikurangi. Setelah 0, pointer dibebaskan.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

lemah_ptr adalah referensi lemah ke pointer bersama yang mengharuskan Anda untuk memeriksa untuk melihat apakah menunjuk-untuk shared_ptr masih ada

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr biasanya digunakan ketika ada ptr pintar pihak ke-3 yang harus Anda gunakan. Ini akan memanggil fungsi gratis untuk menambah dan mengurangi jumlah referensi. Lihat tautan untuk meningkatkan dokumentasi untuk info lebih lanjut.

Doug T.
sumber
Bukankah if (tPtrAccessed[0].get() == 0)seharusnya begitu if (tPtrAccessed.get() == 0) ?
Rajeshwar
@ DougT. Apakah Anda percaya bahwa Java menggunakan ide yang sama dengan Referensi? Lembut, Keras, Lemah dll?
gansub
20

Jangan mengabaikan boost::ptr_containersurvei sembarang petunjuk pintar boost. Mereka bisa sangat berharga dalam situasi di mana misalnya std::vector<boost::shared_ptr<T> >akan terlalu lambat.

timday
sumber
Sebenarnya, terakhir kali saya mencobanya, tolok ukur menunjukkan kesenjangan kinerja telah ditutup secara signifikan sejak saya awalnya menulis ini, setidaknya pada PC HW khas! Pendekatan ptr_container yang lebih efisien mungkin masih memiliki beberapa keuntungan dalam kasus penggunaan khusus.
timday
12

Saya kedua saran tentang melihat dokumentasi. Ini tidak seseram kelihatannya. Dan beberapa petunjuk singkat:

  • scoped_ptr- pointer secara otomatis dihapus ketika keluar dari ruang lingkup. Catatan - tidak ada tugas yang mungkin, tetapi memperkenalkan tidak ada overhead
  • intrusive_ptr - pointer penghitungan referensi tanpa overhead smart_ptr . Namun objek itu sendiri menyimpan jumlah referensi
  • weak_ptr - Bekerja bersama dengan shared_ptr untuk menangani situasi yang mengakibatkan ketergantungan melingkar (baca dokumentasi, dan cari di google untuk gambar yang bagus;)
  • shared_ptr - Penunjuk pintar generik, paling kuat (dan kelas berat) (dari yang ditawarkan oleh boost)
  • Ada juga yang lama auto_ptr, yang memastikan bahwa objek yang ditunjuknya dihancurkan secara otomatis ketika kontrol meninggalkan ruang lingkup. Namun memiliki semantik salinan berbeda dari yang lainnya.
  • unique_ptr- akan datang dengan C ++ 0x

Tanggapan untuk mengedit: Ya

Anonim
sumber
8
Saya datang ke sini karena saya menemukan dokumentasi tambahannya terlalu menakutkan.
Francois Botha