Perbedaan antara std :: make_unique dan std :: unique_ptr dengan yang baru

130

Apakah std::make_uniqueada manfaat efisiensi seperti std::make_shared?

Dibandingkan dengan membangun secara manual std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
NFRCR
sumber
Apakah make_sharedada efisiensi lebih dari sekadar menulis kode tangan panjang?
Ed Heal
9
@ EdHeal Mungkin, karena make_shareddapat mengalokasikan ruang untuk objek dan ruang untuk blok kontrol bersama dalam satu alokasi tunggal. Biaya dari itu adalah bahwa objek tidak dapat dialokasikan secara terpisah dari blok kontrol, jadi jika Anda menggunakan weak_ptrbanyak maka Anda mungkin berakhir menggunakan lebih banyak memori.
bames53
Mungkin ini adalah titik awal yang baik stackoverflow.com/questions/9302296/…
Ed Heal

Jawaban:

140

Motivasi di belakang make_uniqueterutama dua kali lipat:

  • make_uniqueaman untuk membuat temporari, sedangkan dengan penggunaan eksplisit newAnda harus mengingat aturan tentang tidak menggunakan temporari yang tidak disebutkan namanya.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
  • Penambahan make_uniqueakhirnya berarti kita dapat memberi tahu orang untuk 'tidak pernah' menggunakan newdaripada aturan sebelumnya untuk "'tidak pernah' digunakan newkecuali ketika Anda membuat unique_ptr".

Ada juga alasan ketiga:

  • make_uniquetidak membutuhkan penggunaan tipe yang berlebihan. unique_ptr<T>(new T())->make_unique<T>()

Tidak ada alasan yang melibatkan peningkatan efisiensi runtime seperti yang make_shareddilakukan (karena menghindari alokasi kedua, dengan biaya penggunaan memori puncak yang berpotensi lebih tinggi).

* Diharapkan bahwa C ++ 17 akan menyertakan perubahan aturan yang berarti bahwa ini tidak lagi tidak aman. Lihat makalah komite C ++ P0400R0 dan P0145R3 .

bames53
sumber
Akan lebih masuk akal untuk mengatakan std::unique_ptrdan std::shared_ptritulah sebabnya kita dapat memberitahu orang-orang untuk "tidak pernah menggunakan new."
Timothy Shields
2
@TimothyShields Ya, itulah yang saya maksud. Hanya saja di C ++ 11 yang kita miliki make_shareddan begitu make_uniquejuga bagian terakhir yang sebelumnya hilang.
bames53
1
Adakah cara Anda bisa menyebutkan secara singkat, atau menautkan ke, alasan untuk tidak menggunakan temporari yang tidak disebutkan namanya?
Dan Nissenbaum
14
Sebenarnya, dari stackoverflow.com/a/19472607/368896 , saya punya itu ... Dari jawaban itu, mempertimbangkan fungsi berikut panggilan f: f(unique_ptr<T>(new T), function_that_can_throw());- mengutip jawaban: Compiler diperbolehkan untuk panggilan (dalam rangka): new T, function_that_can_throw(), unique_ptr<T>(...). Tentunya jika function_that_can_throwbenar-benar melempar maka Anda bocor. make_uniquemencegah kasus ini. Jadi, pertanyaan saya terjawab.
Dan Nissenbaum
3
Salah satu alasan saya pernah menggunakan std :: unique_ptr <T> (T baru) adalah karena konstruktor T adalah pribadi. Bahkan jika panggilan ke std :: make_unique menggunakan metode pabrik publik kelas T, itu tidak dapat dikompilasi karena salah satu metode mendasar dari std :: make_unique tidak dapat mengakses konstruktor pribadi. Saya tidak ingin berteman dengan metode itu karena saya tidak ingin bergantung pada implementasi std :: make_unique. Jadi satu-satunya solusi adalah, memanggil metode baru di pabrik saya dari kelas T, dan kemudian membungkusnya dengan std :: unique_ptr <T>.
Patrick
14

std::make_uniquedan std::make_sharedapakah ada karena dua alasan:

  1. Sehingga Anda tidak perlu mencantumkan argumen jenis templat secara eksplisit.
  2. Keamanan pengecualian tambahan atas penggunaan std::unique_ptratau std::shared_ptrkonstruktor. (Lihat bagian Catatan di sini .)

Ini bukan tentang efisiensi runtime. Ada sedikit tentang blok kontrol dan Tyang dialokasikan sekaligus, tapi saya pikir itu lebih merupakan bonus dan sedikit motivasi untuk fungsi-fungsi ini ada.

Perisai Timothy
sumber
Mereka juga ada untuk keamanan pengecualian.
0x499602D2
@ 0x499602D2 Dan itu, tambahan yang bagus. Halaman ini berbicara tentang itu.
Timothy Shields
Untuk pembaca masa depan, C ++ 17 tidak memungkinkan untuk interleaving argumen fungsi sehingga argumen untuk keamanan pengecualian tidak berlaku lagi. Alokasi memori untuk dua paralel std::make_sharedakan memastikan bahwa setidaknya satu dari mereka dibungkus dengan smart pointer sebelum alokasi memori lainnya terjadi, karenanya tidak ada kebocoran.
MathBunny
7

Alasan mengapa Anda harus menggunakan std::unique_ptr(new A())atau std::shared_ptr(new A())langsung bukannya std::make_*()tidak dapat mengakses konstruktor kelas di Aluar lingkup saat ini.

Volodymyr Lashko
sumber
0

Pertimbangkan pemanggilan fungsi

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

Misalkan baru A()berhasil, tetapi baru B()melempar pengecualian: Anda menangkapnya untuk melanjutkan eksekusi normal program Anda. Sayangnya, standar C ++ tidak mengharuskan objek A dihancurkan dan memorinya tidak dapat dialokasikan: memori bocor secara diam-diam dan tidak ada cara untuk membersihkannya. Dengan membungkus A dan B ke dalam std::make_uniquesAnda yakin kebocoran tidak akan terjadi:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

Intinya di sini adalah std::make_unique<A>dan std::make_unique<B>sekarang menjadi objek sementara, dan pembersihan objek sementara ditentukan dengan benar dalam standar C ++: destruktor mereka akan dipicu dan memori dibebaskan. Jadi jika Anda bisa, selalu lebih suka mengalokasikan objek menggunakan std::make_uniquedan std::make_shared.

Dhanya Gopinath
sumber