Saya bertanya-tanya apa manfaat yang mungkin dimiliki copy-on-write? Secara alami, saya tidak mengharapkan pendapat pribadi, tetapi skenario praktis dunia nyata di mana itu bisa bermanfaat secara teknis dan praktis dengan cara yang nyata. Dan secara kasat mata maksud saya lebih dari sekadar menyelamatkan Anda dari mengetik &
karakter.
Untuk memperjelas, pertanyaan ini adalah dalam konteks tipe data, di mana penugasan atau penyalinan konstruksi membuat salinan dangkal implisit, tetapi modifikasi untuk itu membuat salinan mendalam tersirat dan menerapkan perubahan untuk itu alih-alih objek aslinya.
Alasan saya bertanya adalah sepertinya saya tidak menemukan manfaat memiliki KK sebagai perilaku implisit default. Saya menggunakan Qt, yang telah menerapkan SAP untuk banyak tipe data, praktis semua yang memiliki beberapa penyimpanan yang dialokasikan secara dinamis. Tetapi bagaimana itu benar-benar bermanfaat bagi pengguna?
Sebuah contoh:
QString s("some text");
QString s1 = s; // now both s and s1 internally use the same resource
qDebug() << s1; // const operation, nothing changes
s1[o] = z; // s1 "detaches" from s, allocates new storage and modifies first character
// s is still "some text"
Apa yang kita menangkan dengan menggunakan KK dalam contoh ini?
Jika semua yang ingin kita lakukan adalah menggunakan operasi const, s1
redundan, mungkin juga digunakan s
.
Jika kami berniat mengubah nilainya, maka SAP hanya menunda salinan sumber daya hingga operasi non-const yang pertama, dengan biaya (walaupun minimal) menambah hitungan ref untuk pembagian implisit dan melepaskan dari penyimpanan bersama. Memang terlihat seperti semua overhead yang terlibat dalam KK tidak ada gunanya.
Tidak jauh berbeda dalam konteks lewat parameter - jika Anda tidak ingin mengubah nilai, lulus sebagai referensi const, jika Anda ingin memodifikasi, Anda juga membuat salinan yang tersirat jika Anda tidak ingin memodifikasi objek asli, atau lulus dengan referensi jika Anda ingin memodifikasinya. Lagi-lagi SAP tampak seperti overhead yang tidak perlu yang tidak mencapai apa-apa, dan hanya menambahkan batasan bahwa Anda tidak dapat mengubah nilai asli bahkan jika Anda mau, karena setiap perubahan akan terlepas dari objek asli.
Jadi tergantung pada apakah Anda tahu tentang SAP atau tidak menyadari hal itu, dapat mengakibatkan kode dengan maksud tidak jelas dan overhead yang tidak perlu, atau perilaku yang benar-benar membingungkan yang tidak sesuai dengan harapan dan membuat Anda menggaruk-garuk kepala.
Bagi saya tampaknya ada solusi yang lebih efisien dan lebih mudah dibaca apakah Anda ingin menghindari salinan yang tidak perlu, atau Anda bermaksud membuatnya. Jadi, di mana manfaat praktis dari KK? Saya berasumsi pasti ada beberapa manfaat karena di dalamnya digunakan dalam kerangka kerja yang populer dan kuat.
Selain itu, dari apa yang saya baca, SAP sekarang secara eksplisit dilarang di pustaka standar C ++. Tidak tahu apakah penipu yang saya lihat ada hubungannya dengan itu, tapi bagaimanapun, pasti ada alasan untuk ini.
[]
operator. Jadi, SAP memungkinkan desain yang buruk - itu kedengarannya tidak terlalu bermanfaat :) Poin di paragraf terakhir tampaknya valid, tapi saya sendiri bukan penggemar berat perilaku implisit - orang cenderung menganggapnya biasa saja, dan kemudian memiliki kesulitan mencari tahu mengapa kode tidak bekerja seperti yang diharapkan, dan terus bertanya-tanya sampai mereka mencari tahu apa yang tersembunyi di balik perilaku implisit.const_cast
sepertinya dapat merusak SAP semudah itu dapat menembus lewat referensi const. Misalnya,QString::constData()
mengembalikan aconst QChar *
-const_cast
itu dan SAPI runtuh - Anda akan mengubah data objek asli.char*
jelas tidak sadar). Adapun perilaku implisit, saya pikir Anda benar, ada masalah dengannya. Desain API adalah keseimbangan konstan antara dua ekstrem. Terlalu implisit, dan orang-orang mulai mengandalkan perilaku khusus seolah-olah itu adalah bagian dari spesifikasi. Terlalu eksplisit, dan API menjadi terlalu berat saat Anda mengekspos terlalu banyak detail mendasar yang tidak terlalu penting, dan tiba-tiba ditulis ke dalam spesifikasi API Anda.string
kelas mendapat perilaku SAP karena desainer kompiler memperhatikan bahwa sejumlah besar kode menyalin string daripada menggunakan const-reference. Jika mereka menambahkan SAP, mereka bisa mengoptimalkan kasus ini dan membuat lebih banyak orang bahagia (dan itu legal, hingga C ++ 11). Saya menghargai posisi mereka: sementara saya selalu lulus string saya dengan referensi const, saya telah melihat semua sampah sintaksis yang hanya mengurangi keterbacaan. Saya benci menulisconst std::shared_ptr<const std::string>&
hanya untuk menangkap semantik yang benar!Untuk string dan semacamnya sepertinya akan pesimis kasus penggunaan yang lebih umum daripada tidak, karena kasus umum untuk string sering adalah string kecil, dan di sana overhead SAP cenderung lebih besar daripada biaya hanya menyalin string kecil. Optimasi buffer kecil jauh lebih masuk akal bagi saya di sana untuk menghindari alokasi tumpukan dalam kasus seperti itu daripada salinan string.
Namun, jika Anda memiliki objek yang lebih berat, seperti android, dan Anda ingin menyalinnya dan hanya mengganti lengan cybernetic-nya, COW tampaknya cukup masuk akal sebagai cara untuk mempertahankan sintaks yang dapat berubah sambil menghindari kebutuhan untuk menyalin seluruh android hanya untuk beri salinan lengan yang unik. Menjadikannya tidak berubah sebagai struktur data yang persisten pada saat itu mungkin lebih unggul, tetapi "SAP parsial" yang diterapkan pada masing-masing bagian Android tampaknya masuk akal untuk kasus ini.
Dalam kasus seperti itu, dua salinan Android akan berbagi / misalnya batang tubuh yang sama, kaki, kepala, leher, bahu, panggul, dll. Satu-satunya data yang akan berbeda di antara mereka dan tidak dibagi adalah lengan yang dibuat unik untuk android kedua pada menimpa lengannya.
sumber
std::vector<std::string>
sebelum kita milikiemplace_back
dan memindahkan semantik di C ++ 11) . Tapi pada dasarnya kami juga menggunakan instancing. Sistem simpul mungkin atau mungkin tidak mengubah data. Kami memiliki hal-hal seperti pass-through node yang tidak melakukan apa-apa dengan input tetapi hanya mengeluarkan salinan (mereka ada untuk organisasi pengguna programnya). Dalam kasus tersebut, semua data disalin dangkal untuk tipe kompleks ...A
disalin dan tidak ada yang dilakukan untuk objekB
, itu adalah salinan dangkal murah untuk tipe data yang kompleks seperti jerat. Sekarang jika kita memodifikasiB
, data yang kita modifikasiB
menjadi unik melalui COW, tetapiA
tidak tersentuh (kecuali untuk beberapa jumlah referensi atom).