Rückkehr stl-Container-Funktionen
Was ist der beste Weg (leistungsmäßig) von der Rückkehr stl-Containern von einer Funktion? Der container zurückgegeben würde enthalten in der Regel mehrere tausend Elemente.
Methode 1:
typedef std::list<Item> ItemContainer;
ItemContainer CreateManyItems() {
ItemContainer result;
//fill the 'result' ...
return result;
}
ItemContainer a = CreateManyItems();
Methode 2:
void CreateManyItems(ItemContainer &output) {
ItemContainer result;
//fill the 'result' ...
output.swap(result);
}
ItemContainer a;
CreateManyItems(a);
Methode 3:
void std::auto_ptr<ItemContainer> CreateManyItems() {
std::auto_ptr<ItemContainer> result(new ItemContainer);
//fill the 'result' ...
return result;
}
std::auto_ptr<ItemContainer> a = CreateManyItems();
Oder gibt es eine bessere Möglichkeit?
- Performance-Weise ? Nicht mit einem
list
... - M.: die Liste ist nur ein Beispiel.
- es ist ein Interessantes Beispiel obwohl. Der erste "Daten-Struktur" so zu sprechen, dass gelehrt wird, in Computer-Wissenschaft-Kurse sind Listen (einzeln-gebunden und doppelt-verlinkte), weil ein array nur so dumm, es nicht gewährleisten, zu erwähnen. Aber als Ergebnis sind die meisten Studenten "Standard" Wahl, wenn es um eine einfache container ist
list
, weil es das erste war, Sie waren gelehrt! - der zweite Punkt ist über die Kosten des move-Konstruktor. Für copy elision, alle Container Verhalten sich gleich. Für die move-Konstruktor obwohl, das ist nicht so, wie die Kosten der Bewegung proportional ist (mehr oder weniger) auf die
sizeof(Container)
. Deshalb Behälter mit wenig interne Größe (list
odervector
) zuordnen, die meisten von deren Inhalt auf dem heap sind Billig, ob eine dummearray
würde vollständig kopiert Bewegen. - M. : Meine ersten C++ - container std::vector. Ich habe es standardmäßig für alles. Jetzt versuche ich Sie einen container verwenden, ich denke, am besten geeignet ist und einfach zu bedienen, in der gegebenen situation. Die Liste hat eine schöne Eigenschaft, die Iteratoren werden nicht dadurch entkräftet, einsetzen.
- genau, das ist seine einzige Vorteil gegenüber anderen, aber es ist definitiv eine, die praktisch sein kann.
Du musst angemeldet sein, um einen Kommentar abzugeben.
None", wenn Sie nur wollen, zu füllen
std::list
mit items, dann können Siestd::fill
oderstd::fill_n
oder oder eine Kombination von standard-Algorithmus-Funktionen.Als nicht klar ist, was/wie genau Sie wollen, füllen Sie Ihre Liste, so kann nicht kommentieren Ihren code genau. Es sei denn, Sie tun etwas sehr spezielle oder komplexe, die nicht mit standard-Algorithmus-Funktionen, bevorzugen die Verwendung von standard-Algorithmus-Funktionen. Und wenn Sie nicht helfen können, nur dann gehen Sie für den ersten Ansatz und der compiler kann optimieren der Weg der return-Wert in Ihrem code eliding die unnötigen Kopien, da die meisten Compiler implementieren RVO.
Finden Sie unter diesen Themen bei wikipedia:
Diese interessante Themen auf stackoverflow selbst:
Einen Artikel von Dave Abrahams:
Möchte ich noch betonen : haben Sie gesehen, alle generischen Funktionen zur Verfügung gestellt von
<algorithm>
header? Wenn nicht, dann würde ich dir empfehlen, auf den ersten Blick in Sie und sehen, ob einer von Ihnen ( oder eine Kombination von Ihnen) tun können, was Sie wollen in Ihrem code.Wenn Sie erstellen möchten, und füllen Sie die Liste, dann können Sie
std::generate()
oderstd::generate_n
Funktion.Ich in der Regel verwenden Sie die Methode 4 (fast identisch zu Methode 2):
a.clear();
am Anfang der Funktion.fill()
den Zustand der container gewährleistet werden sollte unverändert bleiben?Ich würde die Methode 1 zu verwenden und hoffen, dass der compiler optimiert das Weg das kopieren des Rückgabewertes.
Named Return Value Optimization
Methode 1 können profitieren von copy elision, aber die folgende, sehr ähnliche code nicht:
Dies erfordert C++0x-move-Semantik, um effizient zu vermeiden, kopieren Sie den Behälter. Wenn Sie entwerfen Sie Ihre Funktion, die Sie nicht wissen, wie Kunden gehen zu wollen, es zu benutzen, so sich auf copy elision auf diese Weise fangen könnte, jemanden mit einer unangenehmen performance-hit. Ihre fix in C++03 wäre wie folgt, und Sie sollten wachsam sein, um Situationen, in denen Sie es brauchen:
Da dies im Grunde das gleiche wie Methode 2, könnten Sie bieten sowohl 1 und 2, wie überlastungen, die Hinweise zu dem Anrufer, dass Sie sollten denken Sie über eine potenzielle performance-Falle.
Vorausgesetzt, dass die Elemente in der Auflistung nicht beziehen sich auf einander, meine bevorzugte form der API ist wie folgt, aber es hängt davon ab, welche Art von Schnittstelle Sie sind auszusetzen - weil es eine Vorlage für die Umsetzung müssen verfügbar sein, wenn der Anrufer kompiliert ist (es sei denn, Sie können Vorhersagen, im Voraus die Typen, die es verwendet wird, und
extern
diese Spezialisierungen):Nun, wenn der Anrufer es vorziehen, um die Elemente in deque, weil Sie wollen, dass random-access -, dann werden Sie nur brauchen, um zu zwicken, Ihr code:
Oder wenn Sie brauchen Sie nicht in einer Sammlung auf alle, könnten Sie schreiben Sie einen streaming-Algorithmus hat Ihre Arbeit in Ihrer Ausgabe iterator und sparen Sie eine Menge Speicher.
Könnten Sie stattdessen Schreibe eine iterator-Klasse, die generiert die Elemente eines nach dem anderen, aber das neigt ein bisschen fummelig, weil die Ablaufsteuerung ist "inside out", und nicht unbedingt die Anrufer-code Aussehen schöner (Zeuge
istream_iterator
).a.swap(CreateManyItems())
? Wird dass funktionieren?swap
nimmt eine nicht-const Referenz-parameter, und Sie können Sie nicht binden, dass eine temporäre. Aber Sie können rufen Sie die nicht-const-member-Funktionen, die auf eine vorübergehende. Mal wieder C++0x hat etwas zu sagen über diese, ich nehme anstd::list
hat eine rvalue-swap-und dann arbeiten Sie Ihren Weg herum.Methode 1 ist in Ordnung. Es heißt kopieren ellision, und Sie werden finden, dass es automatisch angewendet, um die transform-Methode 1 zu Methode 2 im Grunde auch, aber es ist viel weniger hässlich zu pflegen.
Einer verknüpften Liste? Wenn Sie auch nur vage performance-orientiert, verwenden Sie eine
std::vector
.vector
kostenlos - die meisten der Zeit passt, in der Tat. Einige der Zeit, gibt es nicht.Dieser, Nummer drei ist wahrscheinlich die beste Leistung. Vielleicht etwas besser, und flexibler ist, wäre eine variation von Nummer 2 ohne
swap
- nur Befüllung der container direkt. Natürlich, das wäre nicht exception-sicher.Kapseln entfernt den container, in eine entsprechende Klasse mit dem pimpl-idiom. Dann pass - /Rendite-Klassen von Wert.
Es gibt einen viel besseren Weg, und ich bin überrascht, dass keine der Antworten hat darauf hingewiesen. Sie im Grunde verwenden Sie output-iterator. Dies macht Ihre Funktion unabhängig von container-und viel flexibler.
Hier ist, wie Sie es verwenden: