Bagaimana cara menerapkan konstruktor salinan untuk kelas yang memiliki unique_ptr
variabel anggota? Saya hanya mempertimbangkan C ++ 11.
c++
c++11
unique-ptr
codefx
sumber
sumber
std::vector
.unique_ptr
, Anda mungkin menginginkan konstruktor pindahkan, jika tujuan Anda adalah meletakkan data dalam filestd::vector
. Di sisi lain, standar C ++ 11 secara otomatis membuat konstruktor pemindahan, jadi mungkin Anda memang menginginkan konstruktor salinan ...Jawaban:
Karena
unique_ptr
tidak dapat dibagikan, Anda perlu menyalin kontennya atau mengonversinyaunique_ptr
menjadi fileshared_ptr
.Anda dapat, seperti yang disebutkan NPE, menggunakan move-ctor, bukan copy-ctor, tetapi itu akan menghasilkan semantik yang berbeda di kelas Anda. Seorang pemindah perlu membuat anggotanya dapat dipindahkan secara eksplisit melalui
std::move
:Memiliki satu set lengkap operator yang diperlukan juga mengarah ke
Jika Anda ingin menggunakan kelas Anda dalam a
std::vector
, pada dasarnya Anda harus memutuskan apakah vektor akan menjadi pemilik unik suatu objek, dalam hal ini akan cukup untuk membuat kelas dapat dipindahkan, tetapi tidak dapat disalin. Jika Anda mengabaikan copy-ctor dan copy-assignment, kompilator akan memandu cara Anda menggunakan std :: vector dengan tipe hanya-bergerak.sumber
int
. Jika Anda memilikiunique_ptr<Base>
yang menyimpan aDerived
, di atas akan terpotong.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
ditambah Deleter / informasi mesin fotokopi.Kasus umum bagi seseorang untuk memiliki a
unique_ptr
di kelas adalah dapat menggunakan pewarisan (jika tidak, objek biasa akan sering melakukannya juga, lihat RAII). Untuk kasus ini, sampai saat ini belum ada jawaban yang sesuai di thread ini .Jadi, inilah titik awalnya:
... dan tujuannya adalah, seperti yang dikatakan, membuat dapat
Foo
disalin.Untuk ini, seseorang perlu melakukan salinan mendalam dari penunjuk yang ada untuk memastikan kelas turunan disalin dengan benar.
Ini dapat dilakukan dengan menambahkan kode berikut:
Pada dasarnya ada dua hal yang terjadi di sini:
Yang pertama adalah penambahan konstruktor salin dan pindahkan, yang secara implisit dihapus
Foo
saat konstruktor salinanunique_ptr
dihapus. Konstruktor move dapat ditambahkan hanya dengan= default
... yang hanya untuk memberi tahu kompiler bahwa konstruktor move biasa tidak boleh dihapus (ini berfungsi, karenaunique_ptr
sudah memiliki konstruktor move yang dapat digunakan dalam kasus ini).Untuk pembuat salinan
Foo
, tidak ada mekanisme yang sama karena tidak ada pembuat salinanunique_ptr
. Jadi, seseorang harus membuat yang baruunique_ptr
, mengisinya dengan salinan poin asli, dan menggunakannya sebagai anggota kelas yang disalin.Dalam hal warisan terlibat, salinan penerima asli harus dilakukan dengan hati-hati. Alasannya adalah bahwa melakukan penyalinan sederhana melalui
std::unique_ptr<Base>(*ptr)
kode di atas akan menghasilkan pemotongan, yaitu, hanya komponen dasar objek yang disalin, sedangkan bagian turunannya hilang.Untuk menghindari hal ini, penyalinan harus dilakukan melalui pola klon. Idenya adalah untuk melakukan penyalinan melalui fungsi virtual
clone_impl()
yang mengembalikan aBase*
di kelas dasar. Dalam kelas turunan, bagaimanapun, itu diperpanjang melalui kovarians untuk mengembalikan aDerived*
, dan penunjuk ini menunjuk ke salinan yang baru dibuat dari kelas turunan. Kelas dasar kemudian dapat mengakses objek baru ini melalui penunjuk kelas dasarBase*
, membungkusnya menjadi aunique_ptr
, dan mengembalikannya melaluiclone()
fungsi aktual yang dipanggil dari luar.sumber
unique_ptr
ketika penahanan langsung akan melakukan sebaliknya. Jawabannya ??? Warisan .unique_ptr
lebihoptional
untuk jenis nullable.clone_impl
basis dalam, kompilator tidak akan memberi tahu Anda jika Anda melupakannya di kelas turunan. Anda dapat, bagaimanapun, menggunakan kelas dasar lainCloneable
dan mengimplementasikan virtual murni diclone_impl
sana. Kemudian kompilator akan mengeluh jika Anda melupakannya di kelas turunan.Coba helper ini untuk membuat salinan dalam, dan atasi ketika sumber unique_ptr adalah null.
Misalnya:
sumber
Daniel Frey menyebutkan tentang solusi salinan, saya akan berbicara tentang cara memindahkan unique_ptr
Mereka disebut konstruktor bergerak dan tugas pindah
Anda bisa menggunakannya seperti ini
Anda perlu membungkus a dan c dengan std :: move karena mereka memiliki nama std :: move memberi tahu kompiler untuk mengubah nilai menjadi referensi rvalue apa pun parameternya Dalam pengertian teknis, std :: move adalah analogi dengan sesuatu seperti " std :: rvalue "
Setelah dipindahkan, sumber daya unique_ptr ditransfer ke unique_ptr lain
Ada banyak topik yang mendokumentasikan rvalue referensi; ini cukup mudah untuk memulai .
Edit:
Objek yang dipindahkan akan tetap valid tetapi statusnya tidak ditentukan .
C ++ primer 5, ch13 juga memberikan penjelasan yang sangat bagus tentang bagaimana "memindahkan" objek
sumber
a
setelah memanggil std :: move (a) dib
konstruktor move? Apakah itu benar-benar tidak valid?Saya sarankan gunakan make_unique
sumber
unique_ptr
tidak dapat disalin, hanya dapat dipindahkan.Ini secara langsung akan memengaruhi Test, yang, pada contoh kedua Anda, juga hanya dapat dipindahkan dan tidak dapat disalin.
Faktanya, ada baiknya Anda menggunakan
unique_ptr
yang melindungi Anda dari kesalahan besar.Misalnya, masalah utama dengan kode pertama Anda adalah bahwa penunjuk tidak pernah dihapus yang benar-benar buruk. Katakan, Anda akan memperbaikinya dengan:
Ini juga buruk. Apa yang terjadi, jika Anda menyalin
Test
? Akan ada dua kelas yang memiliki penunjuk yang menunjuk ke alamat yang sama.Ketika salah satu
Test
dihancurkan, itu juga akan menghancurkan penunjuk. Saat detik AndaTest
hancur, ia juga akan mencoba menghapus memori di belakang penunjuk. Tetapi itu telah dihapus dan kami akan mendapatkan beberapa kesalahan runtime akses memori yang buruk (atau perilaku tidak ditentukan jika kami tidak beruntung).Jadi, cara yang benar adalah dengan mengimplementasikan copy constructor dan copy assignment operator, sehingga perilakunya jelas dan kita bisa membuat salinannya.
unique_ptr
jauh di depan kita di sini. Ini memiliki arti semantik: " Sayaunique
, jadi Anda tidak bisa begitu saja menyalin saya. " Jadi, ini mencegah kita dari kesalahan sekarang menerapkan operator yang ada.Anda dapat menentukan copy constructor dan menyalin operator penugasan untuk perilaku khusus dan kode Anda akan berfungsi. Tapi Anda, memang (!), Dipaksa untuk melakukan itu.
Moral cerita: selalu gunakan
unique_ptr
dalam situasi seperti ini.sumber