Es gibt ein bekanntes Bild (Spickzettel) namens "C ++ Container Choice". Es ist ein Flussdiagramm, um den besten Container für die gewünschte Verwendung auszuwählen.
Weiß jemand, ob es bereits eine C ++ 11-Version davon gibt?
Dies ist der vorherige:
Antworten:
Nicht das ich wüsste, aber es kann in Textform gemacht werden, denke ich. Außerdem ist das Diagramm leicht abweichend, da
list
es im Allgemeinen kein so guter Container ist und auch nichtforward_list
. Beide Listen sind sehr spezialisierte Container für Nischenanwendungen.Um ein solches Diagramm zu erstellen, benötigen Sie nur zwei einfache Richtlinien:
Sich um die Leistung zu sorgen, ist normalerweise zunächst nutzlos. Die großen O-Überlegungen spielen erst dann eine Rolle, wenn Sie mit der Bearbeitung einiger Tausender (oder mehr) Gegenstände beginnen.
Es gibt zwei große Kategorien von Containern:
find
Operationund dann können Sie mehrere Adapter auf sie bauen:
stack
,queue
,priority_queue
. Ich werde die Adapter hier draußen lassen, sie sind ausreichend spezialisiert, um erkennbar zu sein.Frage 1: Assoziativ ?
Frage 1.1: Bestellt ?
unordered_
Container, andernfalls das traditionell bestellte Gegenstück.Frage 1.2: Separater Schlüssel ?
map
, andernfalls aset
Frage 1.3: Duplikate ?
multi
, andernfalls nicht.Beispiel:
Angenommen, mir sind mehrere Personen mit einer eindeutigen ID zugeordnet, und ich möchte die Personendaten so einfach wie möglich aus ihrer ID abrufen.
Ich möchte eine
find
Funktion, also einen assoziativen Container1.1. Die Bestellung ist mir egal, also ein
unordered_
Container1.2. Mein Schlüssel (ID) ist von dem Wert, dem er zugeordnet ist, getrennt, daher a
map
1.3. Die ID ist eindeutig, daher sollte sich kein Duplikat einschleichen.
Die endgültige Antwort lautet :
std::unordered_map<ID, PersonData>
.Frage 2: Speicher stabil ?
list
Frage 2.1: Welche ?
list
; aforward_list
ist nur bei geringerem Speicherbedarf nützlich.Frage 3: Dynamisch dimensioniert ?
{ ... }
Syntax) bereitstellen können , verwenden Sie einearray
. Es ersetzt das traditionelle C-Array, verfügt jedoch über praktische Funktionen.Frage 4: Double-Ended ?
deque
, andernfalls avector
.Sie werden feststellen, dass Sie standardmäßig a wählen, es sei denn, Sie benötigen einen assoziativen Container
vector
. Es stellt sich heraus, dass dies auch die Empfehlung von Sutter und Stroustrup ist .quelle
array
erfordert keinen konstruierbaren Standardtyp; 2) Beim Auswählen desmulti
s geht es nicht so sehr darum, dass Duplikate zulässig sind, sondern vielmehr darum, ob es wichtig ist , sie zu behalten (Sie können Duplikate in Nicht-multi
Containern ablegen , es kommt nur vor, dass nur eines aufbewahrt wird).map.find(key)
ist viel schmackhafter alsstd::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));
obwohl, daher ist es semantisch wichtig, dassfind
es sich eher um eine Mitgliedsfunktion als um eine von handelt<algorithm>
. O (1) gegen O (log n) hat keinen Einfluss auf die Semantik. Ich werde das "effizient" aus dem Beispiel entfernen und es durch "leicht" ersetzen.deque
hätte diese Eigenschaft auch?deque
die Elemente nur dann stabil , wenn Sie an beiden Enden drücken / platzen. Wenn Sie in der Mitte mit dem Einfügen / Löschen beginnen, werden bis zu N / 2 Elemente gemischt, um die erzeugte Lücke zu füllen.Ich mag Matthieus Antwort, aber ich werde das Flussdiagramm wie folgt wiederholen:
Wann sollte std :: vector NICHT verwendet werden?
Wenn Sie einen Container mit Material benötigen, verwenden Sie standardmäßig
std::vector
. Daher ist jeder andere Container nur durch die Bereitstellung einer Funktionsalternative zu gerechtfertigtstd::vector
.Konstruktoren
std::vector
erfordert, dass sein Inhalt beweglich konstruierbar ist, da er in der Lage sein muss, die Elemente zu mischen. Dies ist keine schreckliche Belastung für den Inhalt (beachten Sie, dass dank usw. keine Standardkonstruktoren erforderlich sindemplace
). Die meisten anderen Container benötigen jedoch keinen bestimmten Konstruktor (wiederum dankemplace
). Wenn Sie also ein Objekt haben, in dem Sie einen Verschiebungskonstruktor absolut nicht implementieren können , müssen Sie etwas anderes auswählen.A
std::deque
wäre der allgemeine Ersatz, der viele der Eigenschaften von aufweiststd::vector
, aber Sie können ihn nur an beiden Enden der Deque einfügen. Einsätze in der Mitte müssen verschoben werden. Astd::list
stellt keine Anforderungen an seinen Inhalt.Benötigt Bools
std::vector<bool>
ist nicht. Nun, es ist Standard. Dies ist jedoch nichtvector
im üblichen Sinne der Fall, da Operationen, diestd::vector
normalerweise zulässig sind, verboten sind. Und es enthält mit Sicherheit keinbool
s .Wenn Sie also echtes
vector
Verhalten aus einem Container mitbool
s benötigen , werden Sie es nicht erhaltenstd::vector<bool>
. Also musst du mit einem fällig werdenstd::deque<bool>
.Suchen
Wenn Sie Elemente in einem Container finden müssen und das Such-Tag nicht nur ein Index sein kann, müssen Sie möglicherweise
std::vector
zugunsten vonset
und aufgebenmap
. Beachten Sie das Schlüsselwort " may "; Eine Sortierungstd::vector
ist manchmal eine vernünftige Alternative. Oder Boost.Container'sflat_set/map
, das eine sortierte implementiertstd::vector
.Es gibt jetzt vier Variationen davon, jede mit ihren eigenen Bedürfnissen.
map
wenn das Such-Tag nicht mit dem gesuchten Element übereinstimmt. Andernfalls verwenden Sie aset
.unordered
wenn Sie einen haben viele der Elemente in dem Behälter und Suchleistung absolut sein mussO(1)
, stattO(logn)
.multi
Sie diese Option, wenn Sie mehrere Elemente benötigen, um dasselbe Such-Tag zu haben.Bestellung
Wenn Sie einen Container mit Elementen benötigen, der immer nach einer bestimmten Vergleichsoperation sortiert werden soll, können Sie a verwenden
set
. Oder a,multi_set
wenn Sie mehrere Elemente benötigen, um denselben Wert zu haben.Oder Sie können eine sortierte verwenden
std::vector
, aber Sie müssen sie sortiert halten.Stabilität
Wenn Iteratoren und Referenzen ungültig sind, ist dies manchmal ein Problem. Wenn Sie eine Liste von Elementen benötigen, sodass Sie an verschiedenen anderen Stellen Iteratoren / Zeiger auf diese Elemente haben, ist
std::vector
der Ansatz zur Ungültigmachung möglicherweise nicht angemessen. Jeder Einfügevorgang kann je nach aktueller Größe und Kapazität zu einer Ungültigmachung führen.std::list
bietet eine feste Garantie: Ein Iterator und die zugehörigen Referenzen / Zeiger werden nur ungültig, wenn das Element selbst aus dem Container entfernt wird.std::forward_list
ist da, wenn das Gedächtnis ein ernstes Problem ist.Wenn das eine zu starke Garantie ist,
std::deque
bietet sie eine schwächere, aber nützliche Garantie. Die Ungültigerklärung resultiert aus Einfügungen in der Mitte, aber Einfügungen am Kopf oder am Ende führen nur zur Ungültigmachung von Iteratoren , nicht zu Zeigern / Verweisen auf Elemente im Container.Einfügeleistung
std::vector
bietet nur billiges Einsetzen am Ende (und selbst dann wird es teuer, wenn Sie Kapazität blasen).std::list
ist in Bezug auf die Leistung teuer (jedes neu eingefügte Element kostet eine Speicherzuweisung), aber es ist konsistent . Es bietet auch die gelegentlich unverzichtbare Möglichkeit, Gegenstände praktisch ohne Leistungskosten zu mischen und Gegenstände mit anderenstd::list
Behältern des gleichen Typs ohne Leistungsverlust zu handeln. Wenn Sie viel herum mischen müssen , verwenden Siestd::list
.std::deque
Bietet eine zeitlich konstante Einführung / Entfernung an Kopf und Schwanz, aber die Einführung in der Mitte kann ziemlich teuer sein. Wenn Sie also Dinge sowohl von vorne als auch von hinten hinzufügen / entfernen müssen, ist diesstd::deque
möglicherweise das, was Sie benötigen.Es ist zu beachten, dass die Einfügeleistung dank der Verschiebungssemantik
std::vector
möglicherweise nicht mehr so schlecht ist wie früher. Einige Implementierungen implementierten eine Form des semantischen Verschiebens von Elementen (die sogenannte "Swaptimization"), aber jetzt, da das Verschieben Teil der Sprache ist, wird es vom Standard vorgeschrieben.Keine dynamischen Zuordnungen
std::array
ist ein guter Container, wenn Sie möglichst wenige dynamische Zuordnungen wünschen. Es ist nur ein Wrapper um ein C-Array; Dies bedeutet, dass seine Größe zur Kompilierungszeit bekannt sein muss . Wenn Sie damit leben können, dann verwenden Siestd::array
.Davon abgesehen würde das Verwenden
std::vector
und Verwendenreserve
einer Größe für einen Begrenzten genauso gut funktionierenstd::vector
. Auf diese Weise kann die tatsächliche Größe variieren und Sie erhalten nur eine Speicherzuordnung (es sei denn, Sie sprengen die Kapazität).quelle
std::sort
, dass es auchstd::inplace_merge
interessant ist, neue Elemente einfach zu platzieren (anstatt einenstd::lower_bound
+std::vector::insert
Aufruf). Schön zu lernenflat_set
undflat_map
!vector<bool>
istvector<char>
.std::allocator<T>
diese Ausrichtung nicht unterstützt wird (und ich weiß nicht, warum dies nicht der Fall ist), können Sie jederzeit Ihren eigenen benutzerdefinierten Allokator verwenden.std::vector::resize
hat eine Überladung, die keinen Wert annimmt (es nimmt nur die neue Größe an; alle neuen Elemente werden standardmäßig an Ort und Stelle erstellt). Warum können Compiler Wertparameter nicht richtig ausrichten, selbst wenn sie für diese Ausrichtung deklariert sind?bitset
für bool, wenn Sie die Größe im Voraus kennen en.cppreference.com/w/cpp/utility/bitsetHier ist die C ++ 11-Version des obigen Flussdiagramms. [ursprünglich ohne Zuschreibung an seinen ursprünglichen Autor, Mikael Persson, veröffentlicht ]
quelle
Hier ist ein kurzer Dreh, obwohl es wahrscheinlich Arbeit braucht
Sie werden feststellen, dass diese unterscheidet sich wild von der C ++ 03 - Version, in erster Linie aufgrund der Tatsache , dass ich nicht wie verknüpften Knoten wirklich tun. Die Leistung der verknüpften Knotencontainer kann normalerweise von einem nicht verknüpften Container übertroffen werden, außer in einigen seltenen Situationen. Wenn Sie diese Situationen nicht kennen und Zugriff auf Boost haben, verwenden Sie keine verknüpften Knotencontainer. (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset). Diese Liste konzentriert sich hauptsächlich auf kleine und mittlere Container, da (A) das 99,99% des Codes sind und (B) eine große Anzahl von Elementen benutzerdefinierte Algorithmen benötigt, keine unterschiedlichen Container.
quelle