Diadopsi dari sini .
Sebagian besar templat di pustaka standar C ++ mengharuskan mereka dipakai dengan tipe lengkap. Namun shared_ptr
dan unique_ptr
merupakan pengecualian parsial . Beberapa, tetapi tidak semua anggota mereka dapat dipakai dengan tipe tidak lengkap. Motivasi untuk ini adalah untuk mendukung idiom seperti jerawat menggunakan pointer pintar, dan tanpa risiko perilaku yang tidak terdefinisi.
Perilaku tidak terdefinisi dapat terjadi ketika Anda memiliki tipe yang tidak lengkap dan Anda memanggilnya delete
:
class A;
A* a = ...;
delete a;
Kode di atas adalah hukum. Itu akan dikompilasi. Kompiler Anda mungkin atau mungkin tidak memancarkan peringatan untuk kode di atas seperti di atas. Ketika dijalankan, hal-hal buruk mungkin akan terjadi. Jika Anda sangat beruntung program Anda akan macet. Namun hasil yang lebih mungkin adalah bahwa program Anda akan secara diam-diam membocorkan memori karena ~A()
tidak akan dipanggil.
Menggunakan auto_ptr<A>
contoh di atas tidak membantu. Anda masih mendapatkan perilaku tidak terdefinisi yang sama seolah-olah Anda menggunakan pointer mentah.
Meskipun demikian, menggunakan kelas yang tidak lengkap di tempat-tempat tertentu sangat berguna! Di sinilah shared_ptr
dan unique_ptr
membantu. Penggunaan salah satu dari pointer cerdas ini akan memungkinkan Anda untuk pergi dengan tipe yang tidak lengkap, kecuali jika perlu untuk memiliki tipe yang lengkap. Dan yang paling penting, ketika perlu memiliki tipe yang lengkap, Anda mendapatkan kesalahan waktu kompilasi jika Anda mencoba menggunakan smart pointer dengan tipe yang tidak lengkap pada saat itu.
Tidak ada lagi perilaku yang tidak terdefinisi:
Jika kode Anda dikompilasi, maka Anda telah menggunakan tipe lengkap di mana pun Anda perlu.
class A
{
class impl;
std::unique_ptr<impl> ptr_; // ok!
public:
A();
~A();
// ...
};
shared_ptr
dan unique_ptr
membutuhkan jenis yang lengkap di tempat yang berbeda. Alasannya tidak jelas, berkaitan dengan deleter dinamis vs deleter statis. Alasan tepatnya tidak penting. Faktanya, dalam kebanyakan kode tidak terlalu penting bagi Anda untuk mengetahui dengan tepat di mana jenis yang lengkap diperlukan. Hanya kode, dan jika Anda salah, kompiler akan memberi tahu Anda.
Namun, jika berguna bagi Anda, berikut adalah tabel yang mendokumentasikan beberapa anggota shared_ptr
dan unique_ptr
berkenaan dengan persyaratan kelengkapan. Jika anggota membutuhkan tipe yang lengkap, maka entri memiliki "C", jika tidak, entri tabel diisi dengan "I".
Complete type requirements for unique_ptr and shared_ptr
unique_ptr shared_ptr
+------------------------+---------------+---------------+
| P() | I | I |
| default constructor | | |
+------------------------+---------------+---------------+
| P(const P&) | N/A | I |
| copy constructor | | |
+------------------------+---------------+---------------+
| P(P&&) | I | I |
| move constructor | | |
+------------------------+---------------+---------------+
| ~P() | C | I |
| destructor | | |
+------------------------+---------------+---------------+
| P(A*) | I | C |
+------------------------+---------------+---------------+
| operator=(const P&) | N/A | I |
| copy assignment | | |
+------------------------+---------------+---------------+
| operator=(P&&) | C | I |
| move assignment | | |
+------------------------+---------------+---------------+
| reset() | C | I |
+------------------------+---------------+---------------+
| reset(A*) | C | C |
+------------------------+---------------+---------------+
Operasi apa pun yang memerlukan konversi pointer memerlukan tipe yang lengkap untuk keduanya unique_ptr
dan shared_ptr
.
The unique_ptr<A>{A*}
konstruktor bisa lolos dengan lengkap A
hanya jika compiler tidak diperlukan untuk membuat sebuah panggilan untuk ~unique_ptr<A>()
. Misalnya jika Anda meletakkannya unique_ptr
di tumpukan, Anda bisa lolos dengan tidak lengkap A
. Rincian lebih lanjut tentang hal ini dapat ditemukan dalam BarryTheHatchet ini jawaban di sini .
shared_ptr
/unique_ptr
" Tabel di bagian akhir harus menjawab pertanyaan Anda.