Dalam skenario apa saya menggunakan wadah STL tertentu?

185

Saya telah membaca tentang wadah STL dalam buku saya tentang C ++, khususnya bagian tentang STL dan wadahnya. Sekarang saya mengerti masing-masing dan masing-masing dari mereka memiliki sifat khusus mereka sendiri, dan saya hampir menghafal mereka semua ... Tapi apa yang saya belum pahami adalah di mana skenario masing-masing digunakan.

Apa penjelasannya? Kode contoh lebih disukai.

Daniel Sloof
sumber
maksudmu map, vectot, set dll
Thomas Tempelmann
Bahkan melihat diagram ini saya tidak bisa mengatakan apa yang akan menjadi yang terbaik untuk digunakan dalam stackoverflow.com/questions
9329011/…
2
@ sbi: Menghapus tag C ++ Faq dari yang ini dan menambahkannya ke inklusif yang lebih baru dan C ++ 11 Bagaimana cara efisien memilih wadah pustaka Standar di C ++ 11?
Alok Simpan

Jawaban:

338

Lembar cheat ini memberikan ringkasan yang cukup bagus dari berbagai wadah.

Lihat diagram alur di bagian bawah sebagai panduan untuk digunakan dalam berbagai skenario penggunaan:

http://linuxsoftware.co.nz/containerchoice.png

Dibuat oleh David Moore dan berlisensi CC BY-SA 3.0

zdan
sumber
14
Diagram alur ini adalah emas, saya berharap saya memiliki sesuatu seperti itu di c #
Bruno
2
Tautan yang diperbarui: Lembar C ++ Kontainer C ++ .
Bill Door
3
Titik awal harus lebih vectordaripada kosong. stackoverflow.com/questions/10699265/…
eonil
5
Anda sekarang memiliki unordered_mapdan unordered_set(dan berbagai varian mereka) yang tidak ada dalam bagan alur tetapi merupakan pilihan yang baik ketika Anda tidak peduli dengan pesanan tetapi perlu menemukan elemen dengan kunci. Pencarian mereka biasanya O (1) bukan O (log n).
Aidiakapi
2
@ shuttle87 bukan hanya ukuran itu tidak akan pernah bervariasi, tetapi yang lebih penting ukuran itu ditentukan pada waktu kompilasi dan tidak akan pernah berubah.
YoungJohn
188

Berikut adalah diagram alur yang terinspirasi oleh versi David Moore (lihat di atas) yang saya buat, yang mutakhir (kebanyakan) dengan standar baru (C ++ 11). Ini hanya pendapat pribadi saya, itu tidak bisa dibantah, tapi saya pikir itu bisa berharga untuk diskusi ini:

masukkan deskripsi gambar di sini

Mikael Persson
sumber
4
Bisakah Anda membuat aslinya tersedia? Ini adalah bagan yang sangat bagus. Mungkin menempel di blog atau GitHub?
kevinarpe
1
Ini adalah bagan yang sangat bagus. Namun bisakah seseorang menjelaskan kepada saya apa yang dimaksud dengan 'posisi gigih'?
IDDQD
3
@STALKER Posisi persisten berarti bahwa jika Anda memiliki pointer atau iterator ke suatu elemen di dalam container, pointer atau iterator itu akan tetap valid (dan menunjuk ke elemen yang sama) terlepas dari apa yang Anda tambahkan atau hapus dari wadah (selama itu bukan elemen yang dimaksud).
Mikael Persson
1
Ini benar-benar bagan yang hebat, namun saya pikir vector (sorted)agak tidak konsisten dengan yang lain. Ini bukan jenis wadah yang berbeda, hanya saja std::vectortetapi disortir. Yang lebih penting, saya tidak melihat mengapa seseorang tidak dapat menggunakan std::setiterasi yang diperintahkan jika itu adalah perilaku standar iterasi melalui set. Tentu, jika jawabannya adalah berbicara tentang mengakses nilai-nilai wadah melalui tertib [], maka ok Anda hanya dapat melakukannya dengan soted std::vector. Namun dalam kedua kasus tersebut, keputusan harus diambil tepat setelah pertanyaan "diperlukan"
RA
1
@ user2019840 Saya ingin membatasi grafik untuk kontainer standar. Apa yang seharusnya muncul sebagai pengganti "vektor yang disortir" adalah "flat_set" (dari Boost.Container ), atau setara (setiap pustaka atau basis kode utama memiliki setara dengan flat_set, AFAIK). Tapi ini adalah non-standar, dan cukup kelalaian dari STL. Dan alasan mengapa Anda tidak ingin beralih melalui std :: set atau std :: map (setidaknya tidak sering) adalah sangat tidak efisien untuk melakukannya .
Mikael Persson
41

Jawaban sederhana: gunakan std::vectoruntuk semuanya kecuali Anda memiliki alasan kuat untuk melakukan sebaliknya.

Ketika Anda menemukan kasus di mana Anda berpikir, "Wah, std::vectortidak berfungsi dengan baik di sini karena X", lanjutkan berdasarkan X.

David Thornley
sumber
1
Namun .. berhati-hatilah untuk tidak menghapus / menyisipkan item saat iterasi ... gunakan const_iterator sejauh mungkin untuk menghindari ini ..
vrdhn
11
Hmm ... Saya pikir orang terlalu banyak menggunakan vektor. Alasannya adalah, bahwa "tidak berfungsi" -Kasus tidak akan terjadi dengan mudah - jadi orang tetap menggunakan wadah yang paling sering digunakan dan menyalahgunakannya untuk menyimpan daftar, antrian, ... Menurut pendapat saya - yang cocok dengan diagram alur - orang harus memilih wadah berdasarkan penggunaan yang dimaksudkan alih-alih menerapkan "yang tampaknya cocok untuk semua".
Hitam
13
@ Black Point adalah, vektor biasanya lebih cepat bahkan pada operasi yang secara teori harus bekerja lebih lambat.
Bartek Banachewicz
1
@Vardhan std::remove_ifhampir selalu lebih unggul daripada pendekatan "delete during iteration".
fredoverflow
1
Beberapa tolok ukur akan sangat membantu diskusi ini menjadi kurang subyektif.
Felix D.
11

Lihatlah Efektif STL oleh Scott Meyers. Ada baiknya menjelaskan cara menggunakan STL.

Jika Anda ingin menyimpan sejumlah objek yang ditentukan / tidak ditentukan dan Anda tidak akan pernah menghapusnya, maka vektorlah yang Anda inginkan. Ini adalah pengganti default untuk array C, dan berfungsi seperti itu, tetapi tidak meluap. Anda dapat mengatur ukurannya sebelumnya juga dengan cadangan ().

Jika Anda ingin menyimpan jumlah objek yang tidak ditentukan, tetapi Anda akan menambahkan dan menghapusnya, maka Anda mungkin menginginkan daftar ... karena Anda dapat menghapus elemen tanpa memindahkan elemen berikut - tidak seperti vektor. Namun, dibutuhkan lebih banyak memori daripada vektor, dan Anda tidak dapat mengakses elemen secara berurutan.

Jika Anda ingin mengambil banyak elemen dan hanya menemukan nilai unik dari elemen tersebut, membacanya semuanya menjadi satu set akan melakukannya, dan itu akan mengurutkannya untuk Anda juga.

Jika Anda memiliki banyak pasangan nilai kunci, dan Anda ingin mengurutkannya berdasarkan kunci, maka peta itu berguna ... tetapi itu hanya akan menampung satu nilai per kunci. Jika Anda membutuhkan lebih dari satu nilai per kunci, Anda bisa memiliki vektor / daftar sebagai nilai Anda di peta, atau menggunakan multimap.

Itu tidak ada di STL, tetapi ada di pembaruan TR1 ke STL: jika Anda memiliki banyak pasangan nilai kunci yang akan Anda cari dengan kunci, dan Anda tidak peduli dengan pesanan mereka, Anda mungkin ingin menggunakan hash - yaitu tr1 :: unordered_map. Saya telah menggunakannya dengan Visual C ++ 7.1, di mana ia disebut stdext :: hash_map. Ini memiliki pencarian O (1) bukan pencarian O (log n) untuk peta.

Mark Krenitsky
sumber
Saya telah mendengar beberapa anekdot yang sekarang menunjukkan bahwa Microsoft hash_mapbukanlah implementasi yang sangat baik. Saya berharap mereka melakukan yang lebih baik unordered_map.
Mark Ransom
3
Daftar - "Anda tidak dapat mengakses elemen secara berurutan." - Saya pikir maksud Anda Anda tidak dapat mengakses atau mengindeks secara langsung ke suatu elemen ....
Tony Delroy
^ Ya, karena akses sekuensial persis seperti apa yang listdilakukan. Agak mencolok kesalahan di sana.
underscore_d
7

Saya mendesain ulang diagram alur untuk memiliki 3 properti:

  1. Saya pikir wadah STL dibagi menjadi 2 kelas utama. Wadah dasar dan yang memanfaatkan wadah dasar untuk menerapkan kebijakan.
  2. Pada awalnya diagram alur harus membagi proses pengambilan keputusan dengan situasi utama yang harus kita putuskan dan kemudian uraikan pada setiap kasus.
  3. Beberapa kontainer yang diperluas memiliki kemungkinan untuk memilih wadah dasar yang berbeda sebagai wadah dalamnya. Flowchart harus mempertimbangkan situasi di mana masing-masing wadah dasar dapat digunakan.

Diagram alir: masukkan deskripsi gambar di sini

Info lebih lanjut disediakan di tautan ini .

Ebrahim
sumber
5

Poin penting hanya sebentar disebutkan sejauh ini, adalah bahwa jika Anda memerlukan memori yang berdekatan (seperti array C memberikan), maka Anda dapat hanya menggunakan vector, arrayatau string.

Gunakan arrayjika ukuran diketahui pada waktu kompilasi.

Gunakan stringjika Anda hanya perlu bekerja dengan tipe karakter dan membutuhkan string, bukan hanya wadah tujuan umum.

Gunakan vectordalam semua kasus lain ( vectorbagaimanapun juga harus menjadi pilihan default dari wadah).

Dengan ketiganya, Anda dapat menggunakan data()fungsi anggota untuk mendapatkan pointer ke elemen pertama wadah.

Jonathan Wakely
sumber
3

Itu semua tergantung pada apa yang ingin Anda simpan dan apa yang ingin Anda lakukan dengan wadah. Berikut adalah beberapa (sangat tidak lengkap) contoh untuk kelas kontainer yang paling sering saya gunakan:

vector: Tata letak yang ringkas dengan sedikit atau tanpa overhead memori per objek yang terkandung. Efisien untuk diulangi. Menambahkan, menyisipkan, dan menghapus bisa mahal, terutama untuk objek yang kompleks. Murah untuk menemukan objek yang terkandung oleh indeks, misalnya myVector [10]. Gunakan di mana Anda akan menggunakan array di C. Bagus di mana Anda memiliki banyak objek sederhana (misalnya int). Jangan lupa untuk menggunakan reserve()sebelum menambahkan banyak objek ke wadah.

list: Overhead memori kecil per objek yang terkandung. Efisien untuk diulangi. Tambahkan, masukkan, dan hapus itu murah. Gunakan tempat Anda akan menggunakan daftar tertaut di C.

set(dan multiset): overhead memori signifikan per objek yang terkandung. Gunakan tempat Anda perlu mencari tahu dengan cepat apakah wadah itu berisi objek yang diberikan, atau gabungkan wadah secara efisien.

map(dan multimap): overhead memori signifikan per objek yang terkandung. Gunakan tempat Anda ingin menyimpan pasangan nilai kunci dan mencari nilai dengan kunci dengan cepat.

Bagan alur pada lembar contekan yang disarankan oleh zdan menyediakan panduan yang lebih lengkap.

Tawaran
sumber
"Overhead memori kecil per objek yang terkandung" tidak benar untuk daftar. std :: list diimplementasikan sebagai daftar tertaut dua kali lipat dan dengan demikian ia mempertahankan 2 pointer per objek yang disimpan yang tidak diabaikan.
Hanna Khalil
Saya masih akan menghitung dua pointer per objek yang disimpan sebagai "kecil".
Tawaran
dibandingkan dengan apa? std :: forward_list adalah sebuah wadah yang terutama disarankan untuk menyimpan lebih sedikit meta-data per objek (hanya satu pointer). Sedangkan std :: vector menampung 0 meta data per objek. Jadi 2 petunjuk tidak dapat dinegosiasikan dibandingkan dengan wadah lain
Hanna Khalil
Itu semua tergantung pada ukuran benda Anda. Saya sudah mengatakan bahwa vektor memiliki "Tata letak ringkas dengan sedikit atau tanpa memori overhead per objek yang terkandung". Saya masih akan mengatakan daftar memiliki overhead memori kecil dibandingkan dengan mengatur dan memetakan, dan overhead memori sedikit lebih besar daripada vektor. Saya tidak begitu yakin apa gunanya Anda mencoba membuat TBH!
Tawaran
Semua wadah berbasis mode cenderung memiliki overhead yang signifikan karena alokasi dinamis, yang jarang datang secara gratis. Kecuali tentu saja Anda menggunakan pengalokasi khusus.
MikeMB
2

Satu pelajaran yang saya pelajari adalah: Cobalah untuk membungkusnya dalam kelas, karena mengubah jenis wadah suatu hari dapat menghasilkan kejutan besar.

class CollectionOfFoo {
    Collection<Foo*> foos;
    .. delegate methods specifically 
}

Tidak perlu banyak biaya di muka, dan menghemat waktu dalam debugging ketika Anda ingin istirahat setiap kali seseorang melakukan operasi x pada struktur ini.

Datang untuk memilih struktur data yang sempurna untuk suatu pekerjaan:

Setiap struktur data menyediakan beberapa operasi, yang dapat beragam kompleksitas waktu:

O (1), O (lg N), O (N), dll.

Anda pada dasarnya harus mengambil tebakan terbaik, di mana operasi akan dilakukan paling banyak, dan menggunakan struktur data yang memiliki operasi itu sebagai O (1).

Sederhana, bukan (-:

vrdhn
sumber
5
Bukankah ini sebabnya kami menggunakan iterator?
Platinum Azure
@PlatinumAzure Bahkan iterator harus menjadi anggota typedef .. Jika Anda mengubah jenis kontainer, Anda juga harus pergi dan mengubah semua definisi iterator ... ini tetap diperbaiki di c ++ 1x sekalipun!
vrdhn
4
Untuk yang penasaran, ini adalah perbaikan di C ++ 11: auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
Black
1
Apakah typedef Collection<Foo*> CollectionOfFoo;cukup?
Craig McQueen
5
Sangat tidak mungkin bahwa Anda dapat mengubah pikiran nanti dan hanya mendelegasikan ke wadah yang berbeda: Waspadai ilusi kode bebas wadah
fredoverflow
1

Saya memperluas diagram alur Mikael Persson yang fantastis. Saya menambahkan beberapa kategori wadah, wadah array, dan beberapa catatan. Jika Anda ingin salinan Anda sendiri, inilah Gambar Google. Terima kasih, Mikael untuk melakukan pekerjaan dasar! C ++ Pemilih Kontainer

John DiFini
sumber
1

Saya menjawab ini dalam pertanyaan lain yang ditandai sebagai dup yang satu ini. Tetapi saya merasa senang untuk merujuk pada beberapa artikel bagus mengenai keputusan untuk memilih wadah standar.

Seperti @David Thornley menjawab, std :: vector adalah cara untuk pergi jika tidak ada kebutuhan khusus lainnya. Ini adalah saran yang diberikan oleh pencipta C ++, Bjarne Stroustrup di blog 2014.

Berikut ini tautan untuk artikel https://isocpp.org/blog/2014/06/stroustrup-lists

dan mengutip dari yang itu,

Dan, ya, rekomendasi saya adalah menggunakan std :: vector secara default.

Dalam komentar tersebut, pengguna @NathanOliver juga menyediakan blog bagus lainnya, yang memiliki ukuran yang lebih konkret. https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html .

CS Pei
sumber