Saya telah mencari melalui kode sumber Dentang dan saya menemukan potongan ini:
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = std::move(Value);
}
Mengapa saya ingin std::move
sebuah std::shared_ptr
?
Apakah ada titik mentransfer kepemilikan pada sumber daya bersama?
Kenapa aku tidak melakukan ini saja?
void CompilerInstance::setInvocation(
std::shared_ptr<CompilerInvocation> Value) {
Invocation = Value;
}
sumber
Dengan menggunakan
move
Anda menghindari peningkatan, dan kemudian segera menurun, jumlah saham. Itu mungkin menghemat beberapa operasi atom mahal pada hitungan penggunaan.sumber
Langkah operasi (seperti langkah konstruktor) untuk
std::shared_ptr
yang murah , karena mereka pada dasarnya adalah "mencuri pointer" (dari sumber ke tujuan, untuk lebih tepatnya, blok kontrol negara seluruh adalah "dicuri" dari sumber ke tujuan, termasuk informasi jumlah referensi) .Alih-alih menyalin operasi pada
std::shared_ptr
panggilan peningkatan referensi nomor atom (yaitu tidak hanya++RefCount
pada anggotaRefCount
data integer , tetapi misalnya memanggilInterlockedIncrement
pada Windows), yang lebih mahal daripada hanya mencuri pointer / negara.Jadi, analisis dinamika hitungan ref dalam kasus ini secara terperinci:
Jika Anda melewati
sp
nilai dan kemudian mengambil salinan di dalamCompilerInstance::setInvocation
metode, Anda memiliki:shared_ptr
parameter dibuat salinan: ref count atomic increment .shared_ptr
parameter ke dalam data anggota: REF menghitung atom kenaikan .shared_ptr
parameter dirusak: ref count atomic decrement .Anda memiliki dua peningkatan atom dan satu penurunan atom, dengan total tiga operasi atom .
Sebaliknya, jika Anda melewatkan
shared_ptr
parameter dengan nilai dan kemudianstd::move
di dalam metode (seperti yang dilakukan dengan benar dalam kode Dentang), Anda memiliki:shared_ptr
parameter dibuat salinan: ref count atomic increment .std::move
yangshared_ptr
parameter ke dalam data anggota: count ref tidak tidak berubah! Anda hanya mencuri pointer / negara: tidak ada operasi penghitungan atom mahal.shared_ptr
parameter dirusak; tetapi karena Anda pindah di langkah 2, tidak ada yang dirusak, karenashared_ptr
parameternya tidak menunjuk ke apa pun lagi. Sekali lagi, tidak ada penurunan atom yang terjadi dalam kasus ini.Intinya: dalam hal ini Anda hanya mendapatkan satu kenaikan atom hitungan ref, yaitu hanya satu operasi atom .
Seperti yang Anda lihat, ini jauh lebih baik daripada dua peningkatan atom ditambah satu penurunan atom (total tiga operasi atom) untuk kasing.
sumber
compilerInstance.setInvocation(std::move(sp));
maka tidak akan ada kenaikan . Anda bisa mendapatkan perilaku yang sama dengan menambahkan kelebihan yang membutuhkan waktushared_ptr<>&&
tetapi mengapa duplikat ketika Anda tidak perlu.setInvocation(new CompilerInvocation)
, atau seperti yang disebutkan ratchetsetInvocation(std::move(sp))
,. Maaf jika komentar pertama saya tidak jelas, saya sebenarnya mempostingnya secara tidak sengaja, sebelum saya selesai menulis, dan saya memutuskan untuk meninggalkannyaMenyalin suatu
shared_ptr
melibatkan menyalin pointer objek keadaan internal dan mengubah jumlah referensi. Memindahkannya hanya melibatkan penukaran pointer ke penghitung referensi internal, dan objek yang dimiliki, jadi lebih cepat.sumber
Ada dua alasan untuk menggunakan std :: move dalam situasi ini. Sebagian besar tanggapan membahas masalah kecepatan, tetapi mengabaikan masalah penting dengan menunjukkan maksud kode dengan lebih jelas.
Untuk std :: shared_ptr, std :: move dengan jelas menunjukkan transfer kepemilikan pointee, sementara operasi penyalinan sederhana menambah pemilik tambahan. Tentu saja, jika pemilik asli kemudian melepaskan kepemilikan mereka (seperti dengan membiarkan std :: shared_ptr mereka dihancurkan), maka transfer kepemilikan telah dilakukan.
Ketika Anda mentransfer kepemilikan dengan std :: move, sudah jelas apa yang terjadi. Jika Anda menggunakan salinan normal, tidak jelas bahwa operasi yang dimaksud adalah transfer sampai Anda memverifikasi bahwa pemilik aslinya segera melepaskan kepemilikan. Sebagai bonus, implementasi yang lebih efisien adalah mungkin, karena transfer kepemilikan atom dapat menghindari keadaan sementara di mana jumlah pemilik telah meningkat satu (dan perubahan petugas dalam jumlah referensi).
sumber
Setidaknya dengan libstdc ++ Anda harus mendapatkan kinerja yang sama dengan pemindahan dan penugasan karena
operator=
panggilanstd::move
pada pointer yang masuk. Lihat: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr.h#L384sumber