push_back vs emplace_back: Was ist der Unterschied und was sollte ein Anfänger wählen?

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum ist das wichtig?

\n\n

Haben Sie sich jemals gefragt, warum es in C++ zwei Möglichkeiten gibt, ein Element am Ende eines Vektors hinzuzufügen? Warum das Rad neu erfinden, wenn es doch das bewährte push_back gibt? Die Antwort liegt in der Leistung und Flexibilität des modernen C++.

\n\n

Auf Stack Overflow hat diese Frage tausende Aufrufe und hunderte Kommentare gesammelt. Und das nicht ohne Grund: Den Unterschied zwischen push_back und emplace_back zu verstehen, ist nicht nur akademisches Wissen, sondern eine praktische Fähigkeit, die Ihren Code um das 2- bis 3-fache beschleunigen kann. Lassen Sie uns das ein für alle Mal klären.

\n\n

Grundlegender Unterschied: Was sagen die Dokumentationen?

\n\n

Beginnen wir einfach. Beide Methoden fügen ein Element am Ende des Containers (Vektor, Liste, Queue) hinzu. Aber sie tun dies auf unterschiedliche Weise:

\n\n
    \n
  • push_back – nimmt ein fertiges Objekt entgegen (per Wert, Referenz oder Rvalue-Referenz) und kopiert/verschiebt es in den Container.
  • \n
  • emplace_back – nimmt Argumente für den Konstruktor des Objekts entgegen und erstellt es direkt im Container (In-Place-Konstruktion).
  • \n
\n\n

Klingt abstrakt? Schauen wir uns den Code an.

\n\n

Praktisches Beispiel: Eine Klasse erstellen

\n\n

Stellen wir uns vor, wir haben eine einfache Klasse Person:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nclass Person {\npublic:\n    std::string name;\n    int age;\n\n    // Konstruktor\n    Person(const std::string& n, int a) \n        : name(n), age(a) {\n        std::cout << "Konstruktor aufgerufen für " << name << std::endl;\n    }\n\n    // Kopierkonstruktor\n    Person(const Person& other) \n        : name(other.name), age(other.age) {\n        std::cout << "Kopierkonstruktor für " << name << std::endl;\n    }\n\n    // Verschiebekonstruktor\n    Person(Person&& other) noexcept\n        : name(std::move(other.name)), age(other.age) {\n        std::cout << "Verschiebekonstruktor für " << name << std::endl;\n    }\n};
\n\n

Schauen wir nun, was bei Verwendung der verschiedenen Methoden passiert.

\n\n

Variante 1: push_back mit temporärem Objekt

\n\n
int main() {\n    std::vector<Person> people;\n    people.push_back(Person("Alice", 25));\n    return 0;\n}
\n\n

Ausgabe:

\n
Konstruktor aufgerufen für Alice\nVerschiebekonstruktor für Alice
\n\n

Was ist passiert? Zuerst wird ein temporäres Person-Objekt erstellt (erster Konstruktoraufruf). Dann wird dieses Objekt in den Vektor verschoben (Verschiebekonstruktor). Zwei Aufrufe – doppelte Arbeit.

\n\n

Variante 2: push_back mit fertigem Objekt

\n\n
int main() {\n    std::vector<Person> people;\n    Person alice("Alice", 25);\n    people.push_back(alice);  // alice — lvalue\n    return 0;\n}
\n\n

Ausgabe:

\n
Konstruktor aufgerufen für Alice\nKopierkonstruktor für Alice
\n\n

Hier sehen wir eine Kopie, keine Verschiebung. Wenn das Objekt groß ist (z. B. dynamische Arrays enthält), kann das Kopieren teuer sein.

\n\n

Variante 3: emplace_back

\n\n
int main() {\n    std::vector<Person> people;\n    people.emplace_back("Bob", 30);  // Argumente für den Konstruktor übergeben\n    return 0;\n}
\n\n

Ausgabe:

\n
Konstruktor aufgerufen für Bob
\n\n

Nur ein einziger Konstruktoraufruf! Das Person-Objekt wird direkt im Speicher des Vektors erstellt, ohne zwischenzeitliche Kopien oder Verschiebungen.

\n\n

Leistung: Wann ist der Unterschied kritisch?

\n\n

Bei einfachen Typen (int, double, Zeiger) ist der Unterschied vernachlässigbar – der Compiler optimiert alles. Aber bei komplexen Objekten kann der Unterschied erheblich sein:

\n\n
    \n
  • Objekte mit schwerem Kopierkonstruktor (z. B. std::string, Container

Blogs

Buchempfehlungen