Saya mengalami kesulitan memahami penggunaan pointer cerdas sebagai anggota kelas di C ++ 11. Saya telah membaca banyak tentang pointer cerdas dan saya pikir saya mengerti bagaimana unique_ptr
dan shared_ptr
/ weak_ptr
secara umum bekerja. Yang tidak saya mengerti adalah penggunaan sebenarnya. Sepertinya semua orang merekomendasikan menggunakan unique_ptr
sebagai cara untuk pergi hampir sepanjang waktu. Tetapi bagaimana saya mengimplementasikan sesuatu seperti ini:
class Device {
};
class Settings {
Device *device;
public:
Settings(Device *device) {
this->device = device;
}
Device *getDevice() {
return device;
}
};
int main() {
Device *device = new Device();
Settings settings(device);
// ...
Device *myDevice = settings.getDevice();
// do something with myDevice...
}
Katakanlah saya ingin mengganti pointer dengan pointer pintar. A unique_ptr
tidak akan bekerja karena getDevice()
, kan? Jadi saat itulah saya menggunakan shared_ptr
dan weak_ptr
? Tidak bisa menggunakan unique_ptr
? Menurut saya untuk kebanyakan kasus shared_ptr
lebih masuk akal kecuali saya menggunakan pointer dalam lingkup yang sangat kecil?
class Device {
};
class Settings {
std::shared_ptr<Device> device;
public:
Settings(std::shared_ptr<Device> device) {
this->device = device;
}
std::weak_ptr<Device> getDevice() {
return device;
}
};
int main() {
std::shared_ptr<Device> device(new Device());
Settings settings(device);
// ...
std::weak_ptr<Device> myDevice = settings.getDevice();
// do something with myDevice...
}
Apakah itu cara untuk pergi? Terima kasih banyak!
sumber
device
ke konstruktorsettings
, apakah Anda ingin tetap dapat merujuknya dalam lingkup panggilan, atau hanya melaluisettings
? Jika yang terakhir,unique_ptr
berguna. Juga, apakah Anda memiliki skenario di mana nilai baliknyagetDevice()
adalahnull
. Jika tidak, cukup kembalikan referensi.shared_ptr
benar dalam 8/10 kasus. 2/10 lainnya dibagi antaraunique_ptr
danweak_ptr
. Juga,weak_ptr
umumnya digunakan untuk memecah referensi melingkar; Saya tidak yakin penggunaan Anda akan dianggap benar.device
anggota data? Anda pertama-tama harus memutuskan itu.unique_ptr
alih - alih dan menyerahkan kepemilikan ketika memanggil konstruktor, jika saya tahu saya tidak akan memerlukannya lagi untuk saat ini. Tetapi sebagai perancangSettings
kelas saya tidak tahu apakah penelepon ingin menyimpan referensi juga. Mungkin perangkat itu akan digunakan di banyak tempat. Ok, mungkin itu maksud Anda. Dalam hal ini, saya tidak akan menjadi pemilik tunggal dan saat itulah saya akan menggunakan shared_ptr, saya kira. Dan: poin yang sangat pintar menggantikan pointer, tetapi bukan referensi, bukan?Jawaban:
Tidak, belum tentu. Yang penting di sini adalah menentukan kebijakan kepemilikan yang sesuai untuk
Device
objek Anda , yaitu siapa yang akan menjadi pemilik objek yang ditunjuk oleh pointer (pintar) Anda.Apakah itu akan menjadi contoh
Settings
objek saja ? ApakahDevice
objek harus dihancurkan secara otomatis ketikaSettings
objek dihancurkan, atau haruskah itu hidup lebih lama dari objek itu?Dalam kasus pertama,
std::unique_ptr
adalah apa yang Anda butuhkan, karena itu membuatSettings
satu-satunya (unik) pemilik objek runcing, dan satu-satunya objek yang bertanggung jawab atas kehancurannya.Di bawah asumsi ini,
getDevice()
harus mengembalikan pointer mengamati sederhana (pointer mengamati adalah pointer yang tidak membuat objek runcing tetap hidup). Jenis paling sederhana dari mengamati pointer adalah pointer mentah:[ CATATAN 1: Anda mungkin bertanya-tanya mengapa saya menggunakan pointer mentah di sini, ketika semua orang terus mengatakan bahwa pointer mentah itu buruk, tidak aman, dan berbahaya. Sebenarnya, itu adalah peringatan yang berharga, tetapi penting untuk menempatkannya dalam konteks yang benar: pointer mentah buruk ketika digunakan untuk melakukan manajemen memori manual , yaitu mengalokasikan dan membatalkan alokasi objek melalui
new
dandelete
. Ketika digunakan semata-mata sebagai sarana untuk mencapai semantik referensi dan mengoper pointer yang tidak memiliki, mengamati, tidak ada yang secara intrinsik berbahaya dalam pointer mentah, kecuali mungkin karena fakta bahwa seseorang harus berhati-hati untuk tidak merujuk pada pointer yang menggantung. - CATATAN AKHIR 1 ][ CATATAN 2: Seperti yang muncul dalam komentar, dalam kasus khusus ini di mana kepemilikan adalah unik dan objek yang dimiliki selalu dijamin untuk hadir (yaitu anggota data internal
device
tidak akan pernah adanullptr
), fungsigetDevice()
dapat (dan mungkin harus) kembalikan referensi daripada sebuah pointer. Meskipun ini benar, saya memutuskan untuk mengembalikan pointer mentah di sini karena saya maksudkan ini untuk menjadi jawaban singkat bahwa seseorang dapat menggeneralisasi ke kasus di manadevice
bisanullptr
, dan untuk menunjukkan bahwa pointer mentah tidak masalah selama orang tidak menggunakannya untuk manajemen memori manual. - END END 2 ]Situasinya sangat berbeda, tentu saja, jika
Settings
objek Anda seharusnya tidak memiliki kepemilikan eksklusif perangkat. Ini bisa menjadi kasus, misalnya, jika penghancuranSettings
objek tidak boleh menyiratkan penghancuranDevice
objek yang runcing juga.Ini adalah sesuatu yang hanya bisa Anda ketahui sebagai perancang program Anda; Dari contoh yang Anda berikan, sulit bagi saya untuk mengatakan apakah ini masalahnya atau tidak.
Untuk membantu Anda mengetahuinya, Anda dapat bertanya pada diri sendiri apakah ada objek lain selain dari
Settings
yang berhak untuk menjagaDevice
objek tetap hidup selama mereka memegang pointer ke sana, alih-alih hanya menjadi pengamat pasif. Jika memang demikian masalahnya, maka Anda memerlukan kebijakan kepemilikan bersama , yangstd::shared_ptr
menawarkan:Perhatikan, itu
weak_ptr
adalah pointer yang mengamati , bukan pointer yang memiliki - dengan kata lain, itu tidak membuat objek runcing tetap hidup jika semua pointer memiliki lainnya ke objek runcing keluar dari ruang lingkup.Keuntungan dari
weak_ptr
atas pointer mentah biasa adalah bahwa Anda dapat dengan aman mengatakan apakahweak_ptr
ini menggantung atau tidak (yakni apakah itu menunjuk ke sebuah objek yang valid, atau jika objek awalnya menunjuk telah dihancurkan). Ini dapat dilakukan dengan memanggilexpired()
fungsi anggota padaweak_ptr
objek.sumber
weak_ptr
selalu merupakan alternatif untuk pointer pengamatan mentah. Ini lebih aman dalam arti, karena Anda dapat memeriksa apakah itu menggantung sebelum dereferensi, tetapi juga dilengkapi dengan beberapa overhead. Jika Anda dapat dengan mudah menjamin bahwa Anda tidak akan melakukan dereference pada pointer yang tergantung, maka Anda akan baik-baik saja dengan mengamati pointer mentahgetDevice()
mengembalikan referensi, bukan? Jadi penelepon tidak perlu memeriksanullptr
.auto myDevice = settings.getDevice()
akan membuat instance baru dari tipe yangDevice
dipanggilmyDevice
dan menyalin-membangunnya dari yang direferensikan oleh referensi yanggetDevice()
mengembalikan. Jika Anda inginmyDevice
menjadi referensi, Anda perlu melakukannyaauto& myDevice = settings.getDevice()
. Jadi kecuali saya kehilangan sesuatu, kami kembali ke situasi yang sama tanpa menggunakanauto
.unique_ptr
ke klien membuka kemungkinan bahwa klien akan pindah dari sana, sehingga memperoleh kepemilikan dan meninggalkan Anda dengan pointer nol (unik).const_cast
s), saya pribadi tidak akan melakukannya. Ini memperlihatkan detail implementasi, yaitu kenyataan bahwa kepemilikan adalah unik dan diwujudkan melalui aunique_ptr
. Saya melihat hal-hal seperti ini: jika Anda ingin / perlu melewati / mengembalikan kepemilikan, meneruskan / mengembalikan pointer pintar (unique_ptr
ataushared_ptr
, tergantung pada jenis kepemilikan). Jika Anda tidak ingin / perlu meneruskan / mengembalikan kepemilikan, gunakanconst
pointer atau referensi (dengan kualifikasi yang tepat ), sebagian besar tergantung pada apakah argumennya bisa nol atau tidak.week_ptr
hanya digunakan untuk loop referensi. Grafik ketergantungan harus grafik yang diarahkan acyclic. Dalam pointer bersama ada 2 jumlah referensi: 1 untukshared_ptr
s, dan 1 untuk semua pointer (shared_ptr
danweak_ptr
). Ketika semuashared_ptr
s dihapus, pointer dihapus. Ketika pointer diperlukan dariweak_ptr
,lock
harus digunakan untuk mendapatkan pointer, jika ada.sumber
shared_ptr
? Bisakah Anda jelaskan mengapa? Sejauh yang saya mengerti,weak_ptr
tidak harus dihitung karena hanya membuat yang barushared_ptr
ketika beroperasi pada objek (jika objek yang mendasarinya masih ada).lock
. versi boost juga aman untuk penghitungan referensi (delete
disebut hanya sekali).weak_ptr::lock()
mengetahui apakah objek telah kadaluwarsa, ia harus memeriksa "blok kontrol" yang berisi jumlah referensi pertama dan penunjuk ke objek, sehingga blok kontrol tidak boleh dihancurkan sementara adaweak_ptr
benda yang masih digunakan, jadi jumlahweak_ptr
objek harus dilacak, yang merupakan jumlah referensi kedua. Objek hancur ketika hitungan ref pertama turun ke nol, blok kontrol hancur ketika hitungan ref kedua turun ke nol.