Frage zu std::vector
-
Ich habe folgenden Code verbrochen:
#include <iostream> #include <vector> using namespace std; class A { public: A() { cout << "A()" << endl; } A(int i) : value(i) { cout << "A(int i)" << endl; } ~A() { cout << "~A()" << endl; } A& operator=(const A& ra) { cout << "A operator=" << endl; value = ra.value; return *this; } private: int value; }; int main() { // your code goes here A a(42); vector<A> va; va.push_back(a); return 0; }Ausgabe ist:
A(int i) ~A() ~A()Jetzt stellt sich mir natürlich die Frage warum 2mal der Destruktor aufgerufen wird obwohl doch nur ein Objekt A erstellt wurde?
-
Hi,
es wird immer eine Kopie von deinem Wert im Container ablegegt. D.h. push_back macht eine Kopie und fügt diese dem vector hinzu.
Logischerweise wird der vector irgendwann zerstört und damit auch dessen Inhalt. Du hast ein Objekt erstellt und der vector hat eins erstellt, darum 2x Destruktor.
-
Copy Konstruktor. Obligatorisches Recherche Thema;; Rule of Three//Five/Zero.
-
Ups
Davon hatte ich schonmal etwas gelesen *g*
Danke für eure Antworten!
-
edit: hab was verbockt, tschuldige (Ideone Code anders als gepostet):
Hallo, ich finde die Frage berechtigt, da es schon komisch aussieht, 1 Konstruktor und 2 Destruktor...
Hab den Code auch mal laufen lassen und bei mir kommt das raus:A(int i) A(const A) ~A() ~A()Womit kann das zu tun haben? Irgendwelche Optimierungen? Persönlich sieht das für mich plausibler aus: Für jeden Konstruktoraufruf ein Destruktoraufruf. Das war soweit ich weiß mit GCC 5.1.
Ich frage mich (auch), weshalb durch
va.push_back(std::move(a));trotzdem eine Kopie erzeugt wird. Ich hab das immer so verstanden, dass durch move() das Kopieren verhindert wird

LG
-
HarteWare schrieb:
Ich frage mich (auch), weshalb durch
va.push_back(std::move(a));trotzdem eine Kopie erzeugt wird. Ich hab das immer so verstanden, dass durch move() das Kopieren verhindert wird

LG
Warum nimmst du an, dass eine Kopie erzeugt wird?
-
HarteWare schrieb:
Ich hab das immer so verstanden, dass durch move() das Kopieren verhindert wird

Ja, wenn ein entsprechender Move-Konstruktor vorhanden ist, schon… du hast jedoch den Kopier-Zuweisungsoperator selbst bereitgestellt. Daher wird der Move-Konstruktor nicht einmal implizit deklariert:
§12.8/9 schrieb:
If the definition of a class
Xdoes not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
(9.2) —Xdoes not have a user-declared copy assignment operator,
-
@manni: Weil die Ausgabe immernoch so aussah:
A(int i) A(const A) ~A() ~A()@Arcoth: Das würde die Sache erklären (weshalb aber entschied man sich zu solch Handhabung? Warum schadet es einen impliziten move-Konstruktor zu haben, nachdem man explizit einen Kopier-Zuweisungsoperator definiert hat?).
-
HarteWare schrieb:
weshalb aber entschied man sich zu solch Handhabung? Warum schadet es einen impliziten move-Konstruktor zu haben, nachdem man explizit einen Kopier-Zuweisungsoperator definiert hat?
Weil wenn ein eigener Copy Assignment Operator nötig ist, wohl auch ein eigene Move Operationen notwendig sind (siehe Antwort von Nathan). Eine Außnahme ist leider wenn man nur ein paar Logging Informationen ausgeben möchte, um zu sehen was passiert.
-
Nachtrag: In deinem Code werden aber sowieso immer zwei Objekte von A angelegt, egal ob du
va.push_back(a)oderva.push_back(move(a))schreibst. Einmal dein Objekt in der main Funktion und das Objekt im vector. Der Unterschied zwischen diesen beiden Varianten ist, wie das zweite Objekt erstellt wird. Im ersten Fall ist es eine Kopie deines Objekts in der main Funktion und im zweiten Fall wird das Objekt "verschoben" was eigentlich bedeutet, dass die Resourcen übertragen werden. Das Objekt in der main Funktion existiert aber weiterhin, nur ist es jetzt eventuell leer. Bei deiner Klasse, die eh nur einen int speichert bringt ein move aber keinerlei Vorteile, da dieses eine int so oder so kopiert werden muss. Moves sind dann schneller als echte Kopien, wenn deine Klasse z.B. einen Zeiger auf einer große Datenstruktur speichert. Dann reicht es nur den Pointer zu kopieren und beim Quellobjekt auf 0 zu setzen (Warnhinweis: Owning Pointer sind böse und dienen hier nur als einfaches Beispiel, besser arbeitet man mit STL Containern welche auch effizient gemoved werden können).Es gibt aber neben
push_backnoch eine andere Methode mit der du direkt ein Objekt im vector erstellen kannst. Dann wird tatsächlich auch nur ein Objekt erstellt, direkt an der Stelle wo man es auch haben möchte. Die Funktion heißt emplace_back und man übergibt direkt Parameter für den Konstruktor des zu erstellenden Objekts. Also für dein Programm etwa sowas:va.emplace_back(42);
-
@sebi707: Vielen Dank für deine Ausführung, war für mich hilfreich und schlüssig.