Jika saya mendeklarasikan objek yang dibungkus dengan pointer bersama:
std::shared_ptr<myClass> myClassObject(new myClass());
maka saya ingin menyampaikannya sebagai argumen untuk suatu metode:
DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
arg1->someField = 4;
}
Apakah hal di atas hanya menambah jumlah referensi shared_pt dan semuanya keren? Atau apakah itu meninggalkan penunjuk yang menggantung?
Apakah Anda masih harus melakukan ini ?:
DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
(*arg1)->someField = 4;
}
Saya pikir cara ke-2 mungkin lebih efisien karena hanya harus menyalin 1 alamat (sebagai lawan dari seluruh penunjuk cerdas), tetapi cara pertama tampaknya lebih mudah dibaca dan saya tidak mengantisipasi mendorong batas kinerja. Saya hanya ingin memastikan tidak ada yang berbahaya tentang itu.
Terima kasih.
c++
c++11
shared-ptr
c++-faq
Steve H.
sumber
sumber
const std::shared_ptr<myClass>& arg1
DoSomething
benarkah perlu berbagi kepemilikan? Sepertinya ini hanya mengambil referensi saja ...void DoSomething(myClass& arg1)
.shared_ptr<>
Khususnya, Anda harus meneruskan nilai untuk benar-benar berbagi kepemilikan.std::make_shared
tidak hanya lebih efisien, tetapi juga lebih aman daripadastd::shared_ptr
konstruktor.Jawaban:
Tentu, saya bisa bantu soal itu. Saya berasumsi Anda memiliki pemahaman tentang semantik kepemilikan di C ++. Benarkah itu?
Baik.
Oke, saya hanya bisa memikirkan dua alasan untuk
shared_ptr
berargumen:shared_ptr
s.Yang mana yang Anda minati?
Contoh dari fungsi tersebut termasuk
std::static_pointer_cast
, pembanding kustom, atau predikat. Misalnya, jika Anda perlu menemukan semua shared_ptr unik dari vektor, Anda memerlukan predikat seperti itu.Persis.
Iya. Dan jika itu tidak mengubah penunjuk, Anda ingin melewati referensi const. Tidak perlu menyalin karena Anda tidak perlu berbagi kepemilikan. Itu skenario lainnya.
Di mana Anda berbagi kepemilikan? Baik. Bagaimana Anda berbagi kepemilikan dengan
shared_ptr
?Maka fungsi tersebut perlu membuat salinan dari a
shared_ptr
, benar?Tidak, itu pesimisasi. Jika diteruskan oleh referensi, fungsi tidak punya pilihan selain membuat salinan secara manual. Jika itu dilewatkan oleh nilai kompilator akan memilih pilihan terbaik antara salinan dan pemindahan dan melakukannya secara otomatis. Jadi, lewati nilai.
Fungsi tersebut dapat dengan mudah memindahkan
shared_ptr
argumen ke penyimpanannya. Memindahkanshared_ptr
murah karena tidak mengubah jumlah referensi apa pun.Dalam hal ini,
shared_ptr
sama sekali tidak relevan dengan fungsinya. Jika Anda ingin memanipulasi orang yang ditunjuk, ambil orang yang ditunjuk, dan biarkan penelepon memilih semantik kepemilikan yang mereka inginkan.Aturan biasa berlaku. Petunjuk cerdas tidak mengubah apa pun.
Baik.
Ah, kasus tepi yang menarik. Saya tidak berharap itu sering terjadi. Tetapi ketika itu terjadi, Anda dapat meneruskan nilai dan mengabaikan salinannya jika Anda tidak membutuhkannya, atau meneruskan referensi dan membuat salinannya jika Anda membutuhkannya.
Jika Anda berada dalam situasi di mana itu benar-benar penting, Anda dapat memberikan dua kelebihan, satu mengambil referensi nilai konstanta, dan yang lainnya mengambil referensi nilai r. Satu salinan, yang lainnya bergerak. Templat fungsi penerusan sempurna adalah opsi lain.
sumber
shared_ptr<T> ptr;
(yaituvoid f(T&)
, dipanggil denganf(*ptr)
) dan objek tidak hidup lebih lama dari panggilan tersebut. Atau tulis satu di mana Anda melewati nilaivoid f(T)
dan masalah muncul.void f(T&)
dan melibatkan utas. Saya pikir masalah saya adalah bahwa nada jawaban Anda menghalangi kepemilikan shared_ptr's, yang merupakan cara paling aman, paling intuitif dan paling tidak rumit untuk menggunakannya. Saya akan sangat tidak menyarankan seorang pemula untuk mengekstrak referensi ke data yang dimiliki oleh shared_ptr <> kemungkinan penyalahgunaannya bagus dan manfaatnya minimal.Saya pikir orang tidak perlu takut menggunakan pointer mentah sebagai parameter fungsi. Jika fungsi tidak akan menyimpan penunjuk atau mempengaruhi masa pakainya, penunjuk mentah bekerja dengan baik dan mewakili penyebut terkecil yang sama. Pertimbangkan misalnya bagaimana Anda akan meneruskan a
unique_ptr
ke dalam fungsi yang mengambil ashared_ptr
sebagai parameter, baik dengan nilai atau referensi const?void DoSomething(myClass * p); DoSomething(myClass_shared_ptr.get()); DoSomething(myClass_unique_ptr.get());
Pointer mentah sebagai parameter fungsi tidak mencegah Anda menggunakan smart pointer dalam kode panggilan, yang sangat penting.
sumber
DoSomething(*a_smart_ptr)
fun(&x)
denganfun(x)
. Dalam contoh yang terakhir inix
bisa baik dilewatkan dengan nilai atau sebagai ref const. Ini akan seperti yang dinyatakan di atas juga memungkinkan Anda untuk lulusnullptr
jika Anda tidak tertarik dengan output ...Ya, keseluruhan ide tentang shared_ptr <> adalah bahwa beberapa instance dapat menampung pointer mentah yang sama dan memori yang mendasarinya hanya akan dibebaskan ketika instance terakhir shared_ptr <> dihancurkan.
Saya akan menghindari pointer ke shared_ptr <> karena itu mengalahkan tujuan karena Anda sekarang berurusan dengan raw_pointers lagi.
sumber
Nilai lewat dalam contoh pertama Anda aman, tetapi ada idiom yang lebih baik. Lewati referensi const bila memungkinkan - saya akan mengatakan ya bahkan ketika berhadapan dengan petunjuk cerdas. Contoh kedua Anda tidak benar-benar rusak tetapi sangat
!???
. Konyol, tidak mencapai apa-apa dan mengalahkan sebagian dari inti dari petunjuk cerdas, dan akan meninggalkan Anda dalam dunia kesakitan saat Anda mencoba untuk membedakan dan memodifikasi sesuatu.sumber
shared_ptr<>
secara khusus, Anda harus membuat salinan untuk benar-benar berbagi kepemilikan; jika Anda memiliki referensi atau penunjuk ke ashared_ptr<>
maka Anda tidak berbagi apa pun, dan tunduk pada masalah seumur hidup yang sama seperti referensi atau penunjuk normal.shared_ptr
pemanggil dijamin hidup lebih lama dr panggilan fungsi, sehingga fungsi tersebut aman dalam menggunakanshared_ptr<> const &
. Jika penunjuk perlu disimpan untuk lain waktu, penunjuk perlu disalin (pada titik ini salinan harus dibuat, bukan memegang referensi), tetapi tidak perlu mengeluarkan biaya penyalinan kecuali Anda perlu melakukannya Itu. Saya akan pergi untuk melewati referensi konstan dalam kasus ini ....shared_ptr
dari antarmuka dan meneruskanconst
referensi plain ( ) ke objek yang dituju.shared_ptr<>
untuk memulai? Sekarang API Anda benar-benar hanya merepotkan, karena memaksa pemanggil untuk mengubah semantik seumur hidup objek mereka tanpa tujuan hanya untuk menggunakannya. Saya tidak melihat sesuatu yang berharga untuk diadvokasi, selain mengatakan "secara teknis tidak merugikan apa pun." Saya tidak melihat sesuatu yang kontroversial tentang 'jika Anda tidak membutuhkan kepemilikan bersama, jangan gunakanshared_ptr<>
'.dalam fungsi
DoSomething
Anda, Anda mengubah anggota data dari sebuah instance kelasmyClass
jadi apa yang Anda ubah adalah objek yang dikelola (pointer mentah) bukan objek (shared_ptr). Yang berarti pada titik balik fungsi ini semua pointer yang dibagikan ke pointer mentah terkelola akan melihat anggota datanya:myClass::someField
diubah ke nilai yang berbeda.dalam hal ini, Anda meneruskan objek ke fungsi dengan jaminan bahwa Anda tidak mengubahnya (berbicara tentang shared_ptr, bukan objek yang dimiliki).
Ungkapan untuk mengungkapkan ini adalah melalui: ref const, seperti itu
void DoSomething(const std::shared_ptr<myClass>& arg)
Demikian pula Anda memastikan pengguna fungsi Anda, bahwa Anda tidak menambahkan pemilik lain ke daftar pemilik penunjuk mentah. Namun, Anda tetap memiliki kemungkinan untuk mengubah objek pokok yang ditunjukkan oleh penunjuk mentah.
CAVEAT: Artinya, jika seseorang memanggil
shared_ptr::reset
sebelum Anda memanggil fungsi Anda, dan pada saat itu adalah shared_ptr terakhir yang memiliki raw_ptr, maka objek Anda akan dimusnahkan, dan fungsi Anda akan memanipulasi Pointer yang menggantung ke objek yang dihancurkan. SANGAT BERBAHAYA!!!sumber