Ich frage mich, ob das Zurückgeben einer Liste, anstatt einen Zeiger auf eins zurückzugeben, im Hinblick auf die Leistung teuer war, denn wenn ich mich erinnere, hat eine Liste nicht viele Attribute (ist das nicht etwa 3 Zeiger? für die aktuelle Position, eine für den Anfang und eine für das Ende?).Wird eine std :: list teuer?
Antwort
Wenn Sie eine std::list
nach Wert zurückgeben, wird nicht nur der Listenkopf kopiert, sondern ein Listenknoten pro Element in der Liste kopiert. Also ja, für eine große Liste ist es teuer.
Wenn die Liste in der Funktion erstellt wird, die sie zurückgibt, können Sie möglicherweise von der benannten Rückgabewertoptimierung profitieren, um eine unnötige Kopie zu vermeiden. Das ist jedoch spezifisch für Ihren Compiler. Es gilt nie, wenn die Liste zum Beispiel bereits vor dem Aufruf der Funktion existiert (zB wenn es sich um eine Elementvariable eines Objekts handelt).
Ein gängiges Idiom in C++, um die Rückgabe von Containern nach Wert zu vermeiden, ist die Verwendung eines Ausgabe-Iterators als Parameter. Also statt:
std::list<int> getListOfInts() {
std::list<int> l;
for (int i = 0; i < 10; ++i) {
l.push_back(i);
}
return l;
}
Sie tun:
template<typename OutputIterator>
void getInts(OutputIterator out) {
for (int i = 0; i < 10; ++i) {
*(out++) = i;
}
}
Dann wird der Anrufer tut:
std::list<int> l;
getInts(std::back_inserter(l));
Oft, wenn der Compiler inlining und Optimierung abgeschlossen ist, wird der Code ist mehr oder weniger identisch .
Der Vorteil davon ist, dass der Aufrufer nicht an eine bestimmte Sammlung gebunden ist - zum Beispiel kann er die Elemente zu einem Vektor anstatt einer Liste hinzugefügt haben, wenn das für die besonderen Umstände nützlicher ist. Wenn er nur jedes Element einmal sehen muss, anstatt alle zusammen, dann kann er Speicher sparen, indem er sie im Streaming-Modus verarbeitet, indem er einen eigenen Ausgabe-Iterator verwendet.
Die Nachteile sind die gleichen wie bei jedem Vorlagencode: Die Implementierung muss dem Aufrufer zum Zeitpunkt der Kompilierung zur Verfügung stehen, und Sie können am Ende viele "doppelten" Objektcode für mehrere Instanzen der Vorlage erhalten. Natürlich können Sie das gleiche Muster ohne Vorlagen verwenden, indem Sie einen Funktionszeiger (plus einen Benutzerdatenzeiger, falls gewünscht) als Parameter verwenden und ihn einmal mit jedem Element aufrufen oder indem Sie eine abstrakte Klasse von IntVisitor mit einem rein virtuellen Member definieren Funktion, und der Anrufer eine Instanz davon bereitstellen.
[Bearbeiten: T.E.D weist in einem Kommentar darauf hin, dass eine andere Möglichkeit, die Kopie ohne Verwendung von Vorlagen zu vermeiden, darin besteht, dass der Aufrufer eine Liste als Referenz übergibt. Dies funktioniert sicherlich, es gibt dem Anrufer weniger Flexibilität als das Template und ist daher nicht das von der STL verwendete Idiom. Es ist eine gute Option, wenn Sie den oben beschriebenen "Vorteil" nicht haben wollen. Eine der ursprünglichen Absichten hinter der STL besteht jedoch darin, "Algorithmen" (in diesem Fall was auch immer die Werte bestimmt) von "Containern" zu trennen (in diesem Fall die Tatsache, dass die Werte zufällig in einer Liste gespeichert sind, im Gegensatz zu einem Vektor oder einem Array oder einem Selbstsortier-Set oder einfach ausgedruckt, ohne sie zu speichern.]
Ich glaube, der Copy-Konstruktor wird aufgerufen.
Tatsächlich ist es das, aber ich habe mich gefragt, was der Copy-Konstruktor macht.Thief immer noch! –
mein Rat: Kaufen Denken in C++ von Bruce Eckel, die meisten solcher Fragen werden nach dem ersten Durchlauf weggehen ;-) – MadH
Buy Accelerated C++ von Andrew Koenig und Barbara Moo. –
Es kann teuer sein, da es jedes Element in der Liste kopiert. Noch wichtiger ist, dass es ein anderes Verhalten hat: Möchten Sie eine Kopie der Liste oder möchten Sie einen Zeiger auf die ursprüngliche Liste?
Wenn Sie als Wert zurückgeben, wird der Kopierkonstruktor aufgerufen und die Elemente werden nacheinander kopiert. Manchmal werden Sie durch die benannte Wertoptimierung gespeichert, auf die Onebyone hingewiesen hat.
Ihre wichtigsten Optionen, um die Kopie, um sicherzustellen, wird nicht stattfinden, sind:
- Pass in einer Liste von Referenz durch die Funktion ausgefüllt werden. Auf diese Weise teilen Sie der Funktion mit, wo die Daten abgelegt werden sollen, und es muss keine Kopie erstellt werden, da Sie sie an ihrem endgültigen Platz ablegen.
- Ordnen Sie eine Liste auf dem Heap zu und geben Sie sie zurück. Sie sollten es in einem intelligenten Zeiger wie einem std :: auto_ptr oder einem boost :: shared_ptr zurückgeben, um sicherzustellen, dass es gelöscht wird und ausnahmesicher ist.
Es (wie immer) abhängt. Der Kopierkonstruktor kann im folgenden Code durch den Rückgabecode aufgerufen werden oder nicht.
std::list<int> foo() {
std::list<int> bar;
// ...
return bar;
};
Es kann nicht geltend gemacht werden, wenn der Compiler return value optimization gilt. Wenn der Kopierkonstruktor aufgerufen wird, ist es wahrscheinlich relativ zu einem Zeiger für größere Listen teurer, und wenn es nicht aufgerufen wird, ist es schneller, die gerade Liste zurückzugeben (weil es eine dynamische Zuordnung vermeidet)
Persönlich mache ich mir darüber keine Sorgen und gebe die gerade Liste zurück. Dann, nur wenn mein Profiler dies ein Problem sagt, betrachte ich Optimierungen.
lesen Sie auch [this] (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) – andreabedini
- 1. Warum wird eine Ladenladeschranke als teuer angesehen?
- 2. Erweitern std :: list
- 3. C++ std :: Iterator ohne intern: std :: vector oder std :: list
- 4. Wie man eine sortierte std :: list von std :: pair in eine std :: map konvertiert
- 5. Teil Art von std :: list
- 6. Objekt aus Std entfernen :: list
- 7. Kann std :: list nicht mit list :: back() vergleichen?
- 8. Entfernen eines Objekts aus Std :: list
- 9. Wie fügt man eine neue Struktur in std :: list ein?
- 10. C++ 11 bewegen Einfügung für std :: deque oder std :: list
- 11. Wie definiere ich eine generische std :: list eines benutzerdefinierten Typs?
- 12. Warum gibt es keinen Operator [] für eine std :: list?
- 13. std :: list threading push_back, front, popfront
- 14. std :: list resize gibt unerwartete Ergebnisse
- 15. Ist std :: list <> :: sort stable?
- 16. std :: list iterator: get next Element
- 17. const_iterator vs iterator für std :: list
- 18. Ist NSTimer teuer?
- 19. Wie teuer ist Typschluss?
- 20. Wie teuer ist ST_GeomFromText
- 21. std :: list - werden die Iteratoren beim Verschieben invalidiert?
- 22. CPU teuer Javascript
- 23. Wie teuer sind Fäden?
- 24. Wie man einen Iterator std :: list in Schleife mit Inkrement
- 25. Verwendung von Destruktor/Finisierer teuer?
- 26. Verhalten von std :: list: begin() wenn die Liste leer ist
- 27. Zugriff auf das erste Element von std :: list?
- 28. Ist std :: string :: clear und std :: list :: clear löscht Daten aus dem Speicher
- 29. Effizientes Entfernen des letzten Elements von std :: list
- 30. entfernen Elemente mit bestimmten Wert von Std :: list
Danke, ich dachte, es war nur den Kopf kopieren! –
Interessant. Warum nicht einfach die Liste als Referenz weitergeben? –
Angst nicht - eine Kopie einer STL-Sammlung ist eine vollständige Kopie (einschließlich Kopien der Elemente selbst). Dies ist so, dass Sie beim Ändern der Kopie (oder ihrer Elemente) nicht auch das Original ändern. Auch wenn es sich um eine Sammlung von Zeigern handelt, müssen die Zeiger trotzdem kopiert werden, obwohl natürlich die Objekte, auf die gezeigt wird, nicht kopiert werden und immer noch zwischen den Sammlungen "geteilt" sind. –