Sejauh yang saya mengerti, C ++ 14 diperkenalkan std::make_unique
karena, sebagai hasil dari urutan evaluasi parameter tidak ditentukan, ini tidak aman:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(Penjelasan: jika evaluasi pertama-tama mengalokasikan memori untuk pointer mentah, kemudian panggilan g()
dan pengecualian dilemparkan sebelum std::unique_ptr
konstruksi, maka memori tersebut bocor.)
Menelepon std::make_unique
adalah cara untuk membatasi urutan panggilan, sehingga membuat semuanya aman:
f(std::make_unique<MyClass>(param), g()); // Syntax B
Sejak itu, C ++ 17 telah mengklarifikasi urutan evaluasi, membuat Sintaks A aman juga, jadi inilah pertanyaan saya: apakah masih ada alasan untuk menggunakan konstruktor std::make_unique
over std::unique_ptr
di C ++ 17? Bisakah Anda memberikan beberapa contoh?
Sampai sekarang, satu-satunya alasan yang dapat saya bayangkan adalah bahwa ini memungkinkan untuk mengetik MyClass
hanya sekali (dengan asumsi Anda tidak perlu bergantung pada polimorfisme dengan std::unique_ptr<Base>(new Derived(param))
). Namun, itu sepertinya alasan yang cukup lemah, terutama ketika std::make_unique
tidak memungkinkan untuk menentukan penghapus sementara std::unique_ptr
konstruktor melakukannya.
Dan hanya untuk memperjelas, saya tidak menganjurkan untuk menghapus std::make_unique
dari Perpustakaan Standar (menjaganya setidaknya masuk akal untuk kompatibilitas ke belakang), tetapi bertanya-tanya apakah masih ada situasi di mana itu sangat disukai untukstd::unique_ptr
sumber
std::unique_ptr
? Ini bukan argumen untuk dilawanmake_unique
std::make_unique
di tempat pertama, saya tidak berpikir itu akan menjadi alasan yang cukup untuk menambahkannya ke STL, terutama ketika itu adalah sintaks yang kurang ekspresif daripada menggunakan konstruktor, tidak lebihJawaban:
Anda benar bahwa alasan utama dihapus. Masih ada yang tidak menggunakan pedoman baru dan alasan pengetikan yang kurang (tidak harus mengulangi jenis atau menggunakan kata
new
). Memang itu bukan argumen yang kuat tetapi saya sangat suka tidak melihatnew
di kode saya.Juga jangan lupa tentang konsistensi. Anda benar-benar harus menggunakannya
make_shared
sehingga penggunaanmake_unique
itu alami dan sesuai dengan pola. Maka sepele untuk mengubahstd::make_unique<MyClass>(param)
kestd::make_shared<MyClass>(param)
(atau sebaliknya) di mana sintaks A membutuhkan lebih banyak penulisan ulang.sumber
new
saya perlu berhenti dan berpikir: berapa lama pointer ini akan hidup? Apakah saya menanganinya dengan benar? Jika ada pengecualian, apakah semuanya sudah dibersihkan dengan benar? Saya tidak ingin bertanya pada diri sendiri pertanyaan-pertanyaan itu dan membuang waktu saya untuk itu dan jika saya tidak menggunakannyanew
, saya tidak perlu menanyakan pertanyaan-pertanyaan itu.new
. Bukankah ini luar biasa?std::make_shared
- bayangkan kasus di mana objek yang dialokasikan besar dan ada banyakstd::weak_ptr
-s yang menunjuk ke sana: akan lebih baik membiarkan objek tersebut dihapus segera setelah terakhir dibagikan pointer dihancurkan dan hidup hanya dengan area bersama yang kecil.std::make_shared
stackoverflow.com/a/20895705/8414561 di mana memori yang digunakan untuk menyimpan objek tidak dapat dibebaskan sampai yang terakhirstd::weak_ptr
hilang (bahkan jika semuastd::shared_ptr
menunjuk ke sana (dan akibatnya objek itu sendiri) telah dihancurkan).make_unique
membedakanT
dariT[]
danT[N]
,unique_ptr(new ...)
tidak.Anda bisa dengan mudah mendapatkan perilaku tak terdefinisi dengan meneruskan penunjuk yang
new[]
diedit ke aunique_ptr<T>
, atau dengan meneruskan penunjuk yangnew
diedit keunique_ptr<T[]>
.sumber
Alasannya adalah untuk memiliki kode yang lebih pendek tanpa duplikat. Membandingkan
Anda menghemat
MyClass
,new
dan kawat gigi. Biayanya hanya satu karakter yang lebih dalam make dibandingkan dengan ptr .sumber
MyClass
, tetapi saya bertanya-tanya apakah ada alasan yang lebih kuat untuk menggunakannya<MyClass>
bagian dalam varian pertama.std::unique_ptr
itu tidak diizinkan. Ini ada hubungannya dengan membedakanstd::unique_ptr<T>
danstd::unique_ptr<T[]>
Setiap penggunaan
new
harus diaudit secara ekstra hati-hati untuk kebenaran seumur hidup; apakah itu terhapus? Hanya sekali?Setiap penggunaan
make_unique
tidak untuk karakteristik ekstra tersebut; selama objek yang memiliki memiliki masa hidup "benar", itu secara rekursif membuat penunjuk unik memiliki "benar".Sekarang, benar bahwa
unique_ptr<Foo>(new Foo())
identik dalam segala hal 1 denganmake_unique<Foo>()
; itu hanya membutuhkan "grep kode sumber Anda untuk semua penggunaannew
untuk mengauditnya" yang lebih sederhana.1 sebenarnya kebohongan dalam kasus umum. Penerusan sempurna tidaklah sempurna
{}
, default init, array adalah pengecualian.sumber
unique_ptr<Foo>(new Foo)
tidak cukup identik denganmake_unique<Foo>()
... yang terakhir melakukannyanew Foo()
Tapi sebaliknya, ya.std::unique_ptr
itu tidak diizinkan. Jika ada hubungannya dengan membedakanstd::unique_ptr<T>
danstd::unique_ptr<T[]>
Itu tidak cukup baik. Mengandalkan klausul teknis yang baru-baru ini diperkenalkan sebagai jaminan keselamatan bukanlah praktik yang sangat kuat:
new
di tempat lain, misalnya dengan copy-paste contoh Anda.new
tempat lain (ok, memang, tidak banyak peluang untuk itu).Secara umum, sebaiknya kode Anda sesuai / kuat / valid dengan jelas tanpa menggunakan tata bahasa, mencari klausa teknis yang kecil atau tidak jelas dalam standar.
(Ini pada dasarnya adalah argumen yang sama yang saya buat di sini tentang urutan penghancuran tupel.)
sumber
Pertimbangkan fungsi void (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}
Misalkan A () baru berhasil, tetapi B baru () memunculkan pengecualian: Anda menangkapnya untuk melanjutkan eksekusi normal program Anda. Sayangnya, standar C ++ tidak mengharuskan objek A dihancurkan dan memorinya dibatalkan alokasinya: memori bocor secara diam-diam dan tidak ada cara untuk membersihkannya. Dengan membungkus A dan B ke dalam std :: make_uniques Anda yakin kebocoran tidak akan terjadi:
void function (std :: make_unique (), std :: make_unique ()) {...} Intinya di sini adalah bahwa std :: make_unique dan std :: make_unique sekarang menjadi objek sementara, dan pembersihan objek sementara ditentukan dengan benar di standar C ++: penghancurnya akan dipicu dan memori dibebaskan. Jadi jika Anda bisa, selalu pilih untuk mengalokasikan objek menggunakan std :: make_unique dan std :: make_shared.
sumber