"C ++ Kapsayıcı seçimi" adı verilen iyi bilinen bir görüntü (kopya kağıdı) var. İstenen kullanım için en iyi kabı seçmek için bir akış şemasıdır.
Zaten bir C ++ 11 sürümü olup olmadığını bilen var mı?
Bu bir öncekidir:
"C ++ Kapsayıcı seçimi" adı verilen iyi bilinen bir görüntü (kopya kağıdı) var. İstenen kullanım için en iyi kabı seçmek için bir akış şemasıdır.
Zaten bir C ++ 11 sürümü olup olmadığını bilen var mı?
Bu bir öncekidir:
Yanıtlar:
Bildiğimden değil, ancak sanırım metinsel olarak yapılabilir. Ayrıca, grafik biraz kapalıdır, çünkü list
genel olarak böyle iyi bir kap değildir ve ikisi de değildir forward_list
. Her iki liste de niş uygulamalar için çok özel kaplardır.
Böyle bir grafik oluşturmak için sadece iki basit yönerge gerekir:
Performanstan endişe etmek genellikle ilk başta işe yaramaz. Büyük O düşünceleri sadece birkaç binlerce (veya daha fazla) öğeyi işlemeye başladığınızda ortaya çıkar.
İki büyük konteyner kategorisi vardır:
find
operasyonları varve sonra bunların üstüne birkaç adaptörleri inşa edebilirsiniz: stack
, queue
, priority_queue
. Adaptörleri burada bırakacağım, tanınabilecek kadar uzmanlar.
Soru 1: İlişkisel ?
Soru 1.1: Sipariş edildi mi?
unordered_
kap kullanın, aksi takdirde geleneksel sipariş edilen karşılığı kullanın.Soru 1.2: Ayrı Anahtar ?
map
, aksi takdirde aset
Soru 1.3: Kopyalar ?
multi
, aksi halde kullanmayın .Misal:
Kendileriyle ilişkilendirilmiş benzersiz bir kimliği olan birkaç kişim olduğunu ve bir kişinin verilerini kimliğinden olabildiğince basit bir şekilde almak istediğimizi varsayalım.
Bir find
işlev, dolayısıyla ilişkisel bir kap istiyorum
1.1. Siparişi daha az umursamadım, bu yüzden bir unordered_
konteyner
1.2. Anahtarım (ID) ilişkilendirildiği değerden ayrı, bu nedenle birmap
1.3. Kimlik benzersizdir, bu nedenle hiçbir kopya içeri girmemelidir.
Nihai cevaptır: std::unordered_map<ID, PersonData>
.
Soru 2: Bellek kararlı mı?
list
Soru 2.1: Hangisi ?
list
; a forward_list
yalnızca daha az bellek alanı için kullanışlıdır.Soru 3: Dinamik boyutta mı?
{ ... }
sözdizimi kullanarak ) sağlayabilirsiniz , ardından bir array
. Geleneksel C-dizisinin yerini alır, ancak kullanışlı işlevlerle.Soru 4: Çift uçlu mu?
deque
, aksi takdirde a kullanın vector
.Varsayılan olarak, ilişkilendirilebilir bir kapsayıcıya ihtiyacınız olmadığı sürece seçiminizin bir olacağını unutmayın vector
. Aynı zamanda Sutter ve Stroustrup'un tavsiyesi olduğu ortaya çıkıyor .
array
varsayılan yapılandırılabilir bir tür gerektirmez; 2) multi
s'yi seçmek, kopyalara izin verilmesiyle ilgili değildir, ancak onları saklamanın önemli olup olmadığı hakkında daha fazladır (kopyaları multi
kapsayıcılara koyabilirsiniz , sadece bir tanesinin tutulduğu görülür).
map.find(key)
da çok daha lezzetli std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));
, bu yüzden anlamsal olarak, find
bir üye işlevinden ziyade bir üye işlevidir <algorithm>
. O (1) ve O (log n) 'e gelince, anlambilimi etkilemez; Örnekten "verimli bir şekilde" çıkaracağım ve "kolayca" ile değiştireceğim.
deque
bu özelliğe sahip olduğunu düşündüm ?
deque
öğelerinde yalnızca her iki uçtan / it tuşuna basarsanız kararlıdır ; ortaya yerleştirmeye / silmeye başlarsanız, oluşturulan boşluğu doldurmak için en fazla N / 2 öğesi karıştırılır.
Matthieu'nun cevabını seviyorum, ancak akış şemasını şu şekilde yeniden ifade edeceğim:
Varsayılan olarak, bir kapsayıcıya ihtiyacınız varsa kullanın std::vector
. Böylece, diğer her kap sadece bazı işlev alternatifleri sağlayarak haklı çıkar std::vector
.
std::vector
içeriğinin hareket ettirilebilir olmasını gerektirir, çünkü etrafındaki öğeleri karıştırması gerekir. Bu, içeriklerin üzerine yerleştirmek için korkunç bir yük değildir ( vb. Sayesinde varsayılan kuruculara gerek olmadığını unutmayın emplace
). Bununla birlikte, diğer konteynerlerin çoğu herhangi bir özel kurucuya ihtiyaç duymaz (yine sayesinde emplace
). Eğer kesinlikle bir nesne var ise olamaz bir hamle yapıcı uygulamak, o zaman başka bir şey almak zorunda kalacaktır.
A std::deque
, birçok özelliğe sahip olan genel değiştirme olacaktır std::vector
, ancak sadece deque'nin her iki ucuna da ekleyebilirsiniz. Ortadaki uçlar hareket etmeyi gerektirir. A std::list
, içeriğine gerek duymaz.
std::vector<bool>
değil. Standart. Ancak normalde izin verilen vector
operasyonlar std::vector
yasaklandığından , olağan anlamda bir değildir . Ve kesinlikle s içermezbool
.
Bu nedenle, vector
bir bool
s kabından gerçek davranışa ihtiyacınız varsa , bunu elde edemezsiniz std::vector<bool>
. Bu yüzden a std::deque<bool>
.
Bir kapsayıcıdaki öğeleri bulmanız gerekiyorsa ve arama etiketi yalnızca bir dizin std::vector
olamazsa, set
ve işaretinden vazgeçmeniz gerekebilir map
. " May " anahtar kelimesine dikkat edin ; bir sıralama std::vector
bazen makul bir alternatiftir. Veya Boost.Container's flat_set/map
, sıralanan uygular std::vector
.
Şimdi bunların her birinin kendi ihtiyaçları olan dört varyasyonu vardır.
map
Arama etiketi, aradığınız öğeyle aynı şey olmadığında bir kullanın . Aksi takdirde a set
.unordered
olduğunda kullanın ve arama performansının kesinlikle yerine olması gerekir .O(1)
O(logn)
multi
Aynı arama etiketine sahip olmak için birden fazla öğeye ihtiyacınız varsa kullanın .Her zaman belirli bir karşılaştırma işlemine göre sıralanacak bir öğe kabına ihtiyacınız varsa, a set
. Veya multi_set
aynı değere sahip birden fazla öğeye ihtiyacınız varsa.
Veya bir sıralı kullanabilirsiniz std::vector
, ancak sıralı tutmanız gerekir.
Yineleyiciler ve referanslar geçersiz kılındığında bazen endişe kaynağı olur. Başka bir yerdeki bu öğelere yineleyiciler / işaretçiler olacak şekilde bir öğe listesine ihtiyacınız varsa, std::vector
geçersiz kılma yaklaşımına uygun olmayabilir. Herhangi bir takma işlemi, geçerli boyuta ve kapasiteye bağlı olarak geçersizliğe neden olabilir.
std::list
kesin bir garanti sunar: bir yineleyici ve ilişkili referansları / işaretçileri yalnızca öğenin kaptan çıkarılması durumunda geçersiz olur. std::forward_list
eğer bellek ciddi bir endişe ise.
Bu çok güçlü bir garanti ise std::deque
, daha zayıf ama kullanışlı bir garanti sunar. Geçersiz kılma, ortadaki yerleştirmelerden kaynaklanır, ancak baş veya kuyruktaki eklemeler , kaptaki öğelere işaretçiler / referanslar değil, yalnızca yineleyicilerin geçersiz kılınmasına neden olur .
std::vector
sadece sonunda ucuz yerleştirme sağlar (ve hatta o zaman bile, eğer üfleme kapasitesiniz pahalı olur).
std::list
performans açısından pahalıdır (yeni eklenen her öğe bir bellek ayırmaya mal olur), ancak tutarlıdır . Ayrıca, neredeyse hiç performans maliyeti olmadan öğeleri karıştırmak std::list
ve aynı zamanda performans kaybı olmadan aynı tipteki diğer kaplarla ticaret yapmak için vazgeçilmez bir yetenek sunar . Bir şeyleri çok karıştırmanız gerekiyorsa , kullanın std::list
.
std::deque
baş ve kuyrukta sabit zamanlı yerleştirme / çıkarma sağlar, ancak ortadaki yerleştirme oldukça pahalı olabilir. Öyleyse önden ve arkadan bir şeyler eklemeniz / çıkarmanız gerekiyorsa, ihtiyacınız olan şey std::deque
olabilir.
Hareket semantiği sayesinde std::vector
ekleme performansının eskisi kadar kötü olmayabileceğine dikkat edilmelidir. Bazı uygulamalar bir anlam semantik tabanlı öğe kopyalama ("swaptimization" olarak adlandırılır) biçimini uyguladı, ancak şimdi hareket dilin bir parçası olduğu için standart tarafından zorunlu kılındı.
std::array
mümkün olan en az dinamik ayırmayı istiyorsanız iyi bir kapsayıcıdır. Bu sadece bir C-dizisinin etrafını saran bir paket; bu, derleme zamanında boyutunun bilinmesi gerektiği anlamına gelir . Bununla yaşayabiliyorsanız, kullanın std::array
.
Bununla birlikte , bir boyut kullanmak std::vector
ve kullanmak, reserve
sınırlı bir şekilde de işe yarayacaktır std::vector
. Bu şekilde, gerçek boyut değişebilir ve yalnızca bir bellek ayırma elde edersiniz (kapasiteyi patlatmadıkça).
std::sort
da var std::inplace_merge
kolayca (yerine daha yeni unsurlar yerleştirmek için ilginç olan std::lower_bound
+ std::vector::insert
çağrısı). Öğrenmek güzel flat_set
ve flat_map
!
vector<bool>
olduğunu vector<char>
.
std::allocator<T>
hizalamayı desteklemiyorsa (ve neden böyle bir şey yapmayacağını bilmiyorum), her zaman kendi özel ayırıcısını kullanabilirsiniz.
std::vector::resize
bir değer almayan aşırı yüklenmesi vardır (sadece yeni boyutu alır; yeni öğeler varsayılan olarak yerinde oluşturulacaktır). Ayrıca, derleyiciler neden bu hizalamaya sahip oldukları bildirilse bile değer parametrelerini düzgün bir şekilde hizalayamıyor?
bitset
önceden boyutu biliyorsanız bool için en.cppreference.com/w/cpp/utility/bitset
Yukarıdaki akış şemasının C ++ 11 sürümü. [aslen orijinal yazarı Mikael Persson'a atfedilmeden yayınlanmıştır ]
İşte hızlı bir dönüş, muhtemelen işe ihtiyacı var
Should the container let you manage the order of the elements?
Yes:
Will the container contain always exactly the same number of elements?
Yes:
Does the container need a fast move operator?
Yes: std::vector
No: std::array
No:
Do you absolutely need stable iterators? (be certain!)
Yes: boost::stable_vector (as a last case fallback, std::list)
No:
Do inserts happen only at the ends?
Yes: std::deque
No: std::vector
No:
Are keys associated with Values?
Yes:
Do the keys need to be sorted?
Yes:
Are there more than one value per key?
Yes: boost::flat_map (as a last case fallback, std::map)
No: boost::flat_multimap (as a last case fallback, std::map)
No:
Are there more than one value per key?
Yes: std::unordered_multimap
No: std::unordered_map
No:
Are elements read then removed in a certain order?
Yes:
Order is:
Ordered by element: std::priority_queue
First in First out: std::queue
First in Last out: std::stack
Other: Custom based on std::vector?????
No:
Should the elements be sorted by value?
Yes: boost::flat_set
No: std::vector
Bunun , esas olarak bağlantılı düğümleri sevmeme bağlı olarak, C ++ 03 sürümünden çılgınca farklı olduğunu fark edebilirsiniz . Bağlantılı düğüm kapları, nadir görülen durumlar dışında genellikle bağlantısız bir kap tarafından performansta atılabilir. Bu durumların ne olduğunu bilmiyorsanız ve artırmaya erişiminiz varsa, bağlantılı düğüm kaplarını kullanmayın. (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Bu liste çoğunlukla küçük ve orta taraflı kaplara odaklanır, çünkü (A) kodda uğraştığımızın% 99,99'udur ve (B) Çok sayıda öğe farklı kapsayıcılara değil, özel algoritmalara ihtiyaç duyar.