Bagaimana cara mendeklarasikan std :: unique_ptr dan apa gunanya?

95

Saya mencoba memahami cara std::unique_ptrkerjanya dan untuk itu saya menemukan dokumen ini . Penulis memulai dari contoh berikut:

#include <utility>  //declarations of unique_ptr
using std::unique_ptr;
// default construction
unique_ptr<int> up; //creates an empty object
// initialize with an argument
unique_ptr<int> uptr (new int(3));
double *pd= new double;
unique_ptr<double> uptr2 (pd);
// overloaded * and ->
*uptr2 = 23.5;
unique_ptr<std::string> ups (new std::string("hello"));
int len=ups->size();

Yang membingungkan saya adalah di baris ini

unique_ptr<int> uptr (new int(3));

Kami menggunakan integer sebagai argumen (antara tanda kurung siku) dan di sini

unique_ptr<double> uptr2 (pd);

kami menggunakan pointer sebagai argumen. Apakah ada bedanya?

Yang juga tidak jelas bagi saya, adalah bagaimana pointer yang dinyatakan dengan cara ini akan berbeda dari pointer yang dinyatakan dengan cara yang "normal".

Roma
sumber
13
new int(3)mengembalikan sebuah penunjuk ke yang baru int, seperti pdpenunjuk ke yang baru double.
David Schwartz

Jawaban:

89

Konstruktor unique_ptr<T>menerima penunjuk mentah ke objek bertipe T(jadi, ia menerima a T*).

Pada contoh pertama:

unique_ptr<int> uptr (new int(3));

Pointer adalah hasil newekspresi, sedangkan pada contoh kedua:

unique_ptr<double> uptr2 (pd);

Penunjuk disimpan dalam pdvariabel.

Secara konseptual, tidak ada yang berubah (Anda membuat a unique_ptrdari pointer mentah), tetapi pendekatan kedua berpotensi lebih berbahaya, karena akan memungkinkan Anda, misalnya, untuk melakukan:

unique_ptr<double> uptr2 (pd);
// ...
unique_ptr<double> uptr3 (pd);

Dengan demikian memiliki dua penunjuk unik yang secara efektif merangkum objek yang sama (sehingga melanggar semantik penunjuk unik ).

Inilah mengapa formulir pertama untuk membuat penunjuk unik lebih baik, jika memungkinkan. Perhatikan, bahwa dalam C ++ 14 kita dapat melakukan:

unique_ptr<int> p = make_unique<int>(42);

Yang mana lebih jelas dan lebih aman. Sekarang tentang keraguan Anda ini:

Yang juga tidak jelas bagi saya, adalah bagaimana pointer yang dinyatakan dengan cara ini akan berbeda dari pointer yang dinyatakan dengan cara yang "normal".

Pointer cerdas seharusnya memodelkan kepemilikan objek, dan secara otomatis menangani pemusnahan objek yang diarahkan saat pointer terakhir (cerdas, memiliki) ke objek tersebut berada di luar cakupan.

Dengan cara ini Anda tidak perlu mengingat melakukannya deletepada objek yang dialokasikan secara dinamis - destruktor dari smart pointer akan melakukannya untuk Anda - atau khawatir tentang apakah Anda tidak akan membatalkan pointer (menggantung) ke objek yang sudah dihancurkan:

{
    unique_ptr<int> p = make_unique<int>(42);
    // Going out of scope...
}
// I did not leak my integer here! The destructor of unique_ptr called delete

Sekarang unique_ptradalah penunjuk cerdas yang memodelkan kepemilikan unik, yang berarti bahwa setiap saat dalam program Anda hanya akan ada satu penunjuk (yang memiliki) ke objek yang diarahkan - itulah mengapa unique_ptrtidak dapat disalin.

Selama Anda menggunakan petunjuk cerdas dengan cara yang tidak melanggar kontrak implisit yang harus Anda patuhi, Anda akan mendapat jaminan bahwa tidak ada memori yang bocor, dan kebijakan kepemilikan yang tepat untuk objek Anda akan diberlakukan. Petunjuk mentah tidak memberi Anda jaminan ini.

Andy Prowl
sumber
3
Hai, saya tidak mengerti apa-apa tentang model object ownership, integer leakdalam kode atau enforcing ownership policy for object. Bisakah Anda menyarankan topik / sumber daya untuk mempelajari konsep ini?
Nyala udun
1
Saya tidak dapat menggunakan unique_ptr, tanpa mendapatkan kesalahan The text ">" is unexpected. It may be that this token was intended as a template argument list terminator but the name is not known to be a template.:, meskipun saya memiliki #include <utility>dan #include <memory>. Ada saran?
Anonim
15

Tidak ada perbedaan dalam mengerjakan kedua konsep penugasan ke unique_ptr.

int* intPtr = new int(3);
unique_ptr<int> uptr (intPtr);

mirip dengan

unique_ptr<int> uptr (new int(3));

Di sini unique_ptr secara otomatis menghapus ruang yang ditempati uptr.


bagaimana pointer, yang dideklarasikan dengan cara ini akan berbeda dari pointer yang dideklarasikan dengan cara "normal".

Jika Anda membuat integer di ruang heap (menggunakan kata kunci baru atau malloc ), maka Anda harus membersihkan memori itu sendiri ( masing-masing menggunakan delete atau free ).

Pada kode di bawah ini,

int* heapInt = new int(5);//initialize int in heap memory
.
.//use heapInt
.
delete heapInt;

Di sini, Anda harus menghapus heapInt, jika sudah selesai menggunakan. Jika tidak dihapus, maka terjadi kebocoran memori.

Untuk menghindari kebocoran memori seperti itu unique_ptr digunakan, di mana unique_ptr secara otomatis menghapus ruang yang ditempati oleh heapInt saat keluar dari ruang lingkup. Jadi, Anda tidak perlu menghapus atau gratis untuk unique_ptr.

fury.slay
sumber
10

Pointer unik dijamin akan menghancurkan objek yang mereka kelola saat berada di luar jangkauan. http://en.cppreference.com/w/cpp/memory/unique_ptr

Pada kasus ini:

unique_ptr<double> uptr2 (pd);

pdakan hancur saat uptr2keluar dari ruang lingkup. Ini memfasilitasi manajemen memori dengan penghapusan otomatis.

Kasusnya unique_ptr<int> uptr (new int(3));tidak berbeda, kecuali bahwa pointer mentah tidak ditugaskan ke variabel apa pun di sini.

fatihk
sumber
-1

Dari cppreference , salah satu std::unique_ptrkonstruktornya adalah

unique_ptr eksplisit (pointer p) noexcept;

Jadi untuk membuat baru std::unique_ptradalah meneruskan pointer ke konstruktornya.

unique_ptr<int> uptr (new int(3));

Atau sama dengan

int *int_ptr = new int(3);
std::unique_ptr<int> uptr (int_ptr);

Bedanya, Anda tidak perlu membersihkan setelah menggunakannya. Jika Anda tidak menggunakan std::unique_ptr(penunjuk cerdas), Anda harus menghapusnya seperti ini

delete int_ptr;

ketika Anda tidak lagi membutuhkannya atau itu akan menyebabkan kebocoran memori.

Tevada
sumber