Beberapa waktu yang lalu, saya berdiskusi dengan seorang kolega tentang cara memasukkan nilai dalam peta STL . Saya lebih suka
map[key] = value;
karena rasanya alami dan jelas untuk membaca sedangkan dia lebih suka
map.insert(std::make_pair(key, value))
Saya hanya bertanya kepadanya dan kami berdua tidak dapat mengingat alasan mengapa memasukkan lebih baik, tetapi saya yakin itu bukan hanya preferensi gaya melainkan ada alasan teknis seperti efisiensi. The referensi SGI STL hanya mengatakan "Sebenarnya, fungsi anggota ini tidak diperlukan. Itu ada hanya untuk kenyamanan"
Adakah yang bisa memberi tahu saya alasan itu, atau saya hanya bermimpi ada satu?
Jawaban:
Ketika Anda menulis
tidak ada cara untuk mengetahui apakah Anda diganti yang
value
untukkey
, atau jika Anda dibuat barukey
denganvalue
.map::insert()
hanya akan membuat:Untuk sebagian besar aplikasi saya, saya biasanya tidak peduli apakah saya membuat atau mengganti, jadi saya menggunakan yang lebih mudah dibaca
map[key] = value
.sumber
(res.first)->second
daripadavalue
juga dalam kasus kedua.else
karena saya pikir menggunakanvalue
lebih jelas daripada iterator. Hanya jika tipe nilai memiliki copy ctor atau op == yang tidak biasa itu akan berbeda, dan tipe itu akan menyebabkan masalah lain menggunakan wadah STL seperti peta.map.insert(std::make_pair(key,value))
seharusnyamap.insert(MyMap::value_type(key,value))
. Jenis yang dikembalikan darimake_pair
tidak cocok dengan jenis yang diambil olehinsert
dan solusi saat ini membutuhkan konversioperator[]
, cukup membandingkan ukuran sebelum dan sesudahnya. Saya hanya bisa memanggilmap::operator[]
untuk jenis konstruktif default jauh lebih penting.Keduanya memiliki semantik yang berbeda ketika datang ke kunci yang sudah ada di peta. Jadi mereka tidak bisa dibandingkan secara langsung.
Tetapi versi operator [] memerlukan nilai konstruk default, dan kemudian menetapkan, jadi jika ini lebih mahal daripada menyalin konstruksi, maka itu akan lebih mahal. Terkadang konstruksi default tidak masuk akal, dan kemudian tidak mungkin menggunakan versi operator [].
sumber
Hal lain yang perlu diperhatikan
std::map
:myMap[nonExistingKey];
akan membuat entri baru di peta, dikunci untuknonExistingKey
diinisialisasi ke nilai default.Ini membuatku takut saat pertama kali aku melihatnya (sambil membenturkan kepalaku ke bug warisan yang mengerikan). Tidak akan diharapkan itu. Bagi saya, itu terlihat seperti operasi, dan saya tidak mengharapkan "efek samping." Lebih suka
map.find()
saat mendapatkan dari peta Anda.sumber
Jika hit kinerja dari konstruktor default tidak menjadi masalah, silakan, untuk cinta tuhan, pergi dengan versi yang lebih mudah dibaca.
:)
sumber
insert
lebih baik dari sudut keamanan pengecualian.Ekspresi
map[key] = value
sebenarnya adalah dua operasi:map[key]
- membuat elemen peta dengan nilai default.= value
- menyalin nilai ke elemen itu.Pengecualian dapat terjadi pada langkah kedua. Akibatnya operasi hanya akan dilakukan sebagian (elemen baru ditambahkan ke peta, tetapi elemen itu tidak diinisialisasi dengan
value
). Situasi ketika suatu operasi tidak lengkap, tetapi keadaan sistem dimodifikasi, disebut operasi dengan "efek samping".insert
operasi memberikan jaminan yang kuat, berarti tidak memiliki efek samping ( https://en.wikipedia.org/wiki/Exception_safety ).insert
baik dilakukan sepenuhnya atau meninggalkan peta dalam keadaan tidak dimodifikasi.http://www.cplusplus.com/reference/map/map/insert/ :
sumber
Jika aplikasi Anda sangat kritis, saya akan menyarankan menggunakan operator [] karena itu membuat total 3 salinan dari objek asli yang 2 di antaranya adalah objek sementara dan cepat atau lambat dihancurkan sebagai.
Namun dalam sisipan (), 4 salinan dari objek asli dibuat dari mana 3 adalah objek sementara (tidak harus "temporer") dan dihancurkan.
Yang berarti waktu tambahan untuk: 1. Alokasi memori satu objek 2. Satu panggilan konstruktor ekstra 3. Satu panggilan destruktor tambahan 4. Satu objek memori deallokasi
Jika objek Anda besar, konstruktor adalah tipikal, destruktor melakukan banyak pembebasan sumber daya, poin di atas bahkan lebih diperhitungkan. Mengenai keterbacaan, saya pikir keduanya cukup adil.
Pertanyaan yang sama muncul di benak saya tetapi tidak terlalu mudah dibaca tetapi kecepatan. Berikut adalah contoh kode yang saya gunakan untuk mengetahui poin yang saya sebutkan.
sumber
insert
harus melakukan pencarian yang sama, jadi tidak ada perbedaan dari itu[]
(karena kunci peta unik).Sekarang di c ++ 11 saya berpikir bahwa cara terbaik untuk memasukkan pasangan dalam peta STL adalah:
The Hasilnya akan menjadi pasangan dengan:
Elemen pertama (result.first), menunjuk ke pasangan yang dimasukkan atau menunjuk ke pasangan dengan kunci ini jika kunci sudah ada.
Elemen kedua (result.second), benar jika penyisipan itu benar atau salah itu ada sesuatu yang salah.
PS: Jika Anda tidak perlu memesan, Anda dapat menggunakan std :: unordered_map;)
Terima kasih!
sumber
Gotcha dengan map :: insert () adalah tidak akan mengganti nilai jika kunci sudah ada di peta. Saya telah melihat kode C ++ yang ditulis oleh programmer Java di mana mereka mengharapkan insert () untuk berperilaku seperti Map.put () di Java di mana nilainya diganti.
sumber
Satu catatan adalah bahwa Anda juga dapat menggunakan Boost. Tugas :
sumber
Berikut adalah contoh lain, menunjukkan bahwa
operator[]
menimpa nilai untuk kunci jika ada, tetapi.insert
tidak menimpa nilai jika ada.sumber
Ini adalah kasus yang agak terbatas, tetapi menilai dari komentar yang saya terima saya pikir itu perlu diperhatikan.
Saya pernah melihat orang di masa lalu menggunakan peta dalam bentuk
untuk menghindari kasus penimpaan nilai yang tidak disengaja, tetapi kemudian lanjutkan menulis dalam beberapa bit kode lainnya:
Alasan mereka untuk melakukan ini seingat saya adalah karena mereka yakin bahwa dalam bit kode tertentu mereka tidak akan menimpa nilai peta; karenanya, maju dengan metode yang lebih 'mudah dibaca'
[]
.Saya tidak pernah benar-benar mengalami masalah langsung dari kode yang ditulis oleh orang-orang ini, tetapi saya sangat merasa sampai hari ini bahwa risiko - betapapun kecilnya - tidak boleh diambil ketika mereka dapat dengan mudah dihindari.
Dalam kasus di mana Anda berurusan dengan nilai peta yang benar - benar tidak boleh ditimpa, gunakan
insert
. Jangan membuat pengecualian hanya untuk keterbacaan.sumber
insert
(bukaninput
), karenaconst_cast
akan menyebabkan nilai sebelumnya ditimpa, yang sangat non-const. Atau, jangan tandai tipe nilai sebagaiconst
. (Hal semacam itu biasanya merupakan hasil akhirconst_cast
, jadi hampir selalu ada bendera merah yang menunjukkan kesalahan di tempat lain.)insert
dalam kasus di mana Anda ingin mencegah nilai ditimpa. (Baru saja mengubahinput
keinsert
- terima kasih)const_cast<T>(map[key])
adalah 1. [] lebih mudah dibaca, 2. mereka percaya pada bit kode tertentu mereka tidak akan menimpa nilai, dan 3. mereka tidak ingin bit lain dari kode yang tidak diketahui menimpa nilai - nilai mereka - karenanyaconst value
.const_cast
tampaknya lebih dari meniadakan "keterbacaan" tambahan[]
, dan kepercayaan semacam itu hampir cukup untuk memecat seorang pengembang. Kondisi runtime yang rumit diselesaikan dengan desain antipeluru, bukan firasat.Fakta bahwa
insert()
fungsi std :: map tidak menimpa nilai yang terkait dengan kunci memungkinkan kita untuk menulis kode enumerasi objek seperti ini:Ini adalah masalah yang cukup umum ketika kita perlu memetakan objek non-unik yang berbeda ke beberapa id dalam rentang 0..N. Id tersebut dapat digunakan nanti, misalnya, dalam algoritma grafik. Alternatif dengan
operator[]
akan terlihat kurang mudah dibaca menurut saya:sumber