Destruktor wird öfter aufgerufen als Konstruktor
-
Hallo!
Ich habe eine Frage zu diesem Quelltext:
#include <iostream> #include <vector> class A { public: A() { std::cout << "Konstruktor" << std::endl; } ~A() { std::cout << "Destruktor" << std::endl; } }; int main() { std::vector<A> v; v.push_back(A()); }Es wird einmal "Konstruktor" ausgegeben und dannach 3 mal "Destruktor". Bitte erklärt mir mal wie das sein kann.

-
Beim push_back wird das Objekt kopiert -> der Copy-Konstructor wird aufgerufen:
A(const A&) { std::cout << "Copy-Konstruktor" << std::endl; }v.push_back(A());Das ist als würdest du schreiben:
A a; // Konstruktor v.push_back(a); // Copy-KonstruktorAllerdings dürfte nur zwei mal der Destruktor aufgerufen werden und nicht drei mal.
-
Mit diesem Programm sieht man das deutlich:
#include <iostream> #include <vector> #include <conio.h> // wegen getch() class A { public: A() { std::cout << "Konstruktor" << std::endl; } ~A() { std::cout << "Destruktor" << std::endl; } A(const A&) { std::cout << "Copy-Konstruktor" << std::endl; } }; int main() { { std::vector<A> v; getch(); A obj; // Konstruktor getch(); v.push_back(obj); // Copy-Konstruktor getch(); } // 2 x Destruktor getch(); }
-
Vielen Dank Leute. An den Copy-Konstruktor hatte ich garnicht gedacht.

Jetzt werde ich mich erstmal näher darüber informieren.Aber der Copy-Konstruktor wird 2x aufgerufen, auch bei Erhards Code. Deshalb sind die 3 Destruktor Aufrufe ja berechtigt (1x Konstruktor, 2x Copy-Konstruktor), aber wo findet der 2. Copy-Konstruktor Aufruf statt?
-
C++ Frischling schrieb:
Aber der Copy-Konstruktor wird 2x aufgerufen
Nee, nicht in diesen beiden Beispielen.
Ansonsten stimmt da was nicht...
-
Also ich habe zu meinem Anfangscode einfach noch den Copy-Konstruktor hinzugefügt und getestet.
Konstruktor
Copy-Konstruktor
Copy-Konstruktor
Destruktor
Destruktor
DestruktorBei Erhards Code, wie gesagt, genau das gleiche. Ich nutze Visual C++ 7.1.

-
Okay, mit Dev-C++ (g++ 3.2) kommt deine erwartete Ausgabe:
Konstruktor
Copy-Konstruktor
Destruktor
DestruktorAber wie kann es kommen das beim Visual C++ 7.1 ein zusätzlicher Copy-Konstruktor Aufruf generiert wird?
-
Mit MSVC++ 6.0 wird der Ctor und Copycon nur einmal aufgerufen, der Dtor zweimal. Das reicht doch auch völlig.

-
Liegt wohl an der Implementierung von std::vector<T>. Setz doch mal einen Breakpoint auf den Copycon und wenn er stehen bleibt schaust Du im Aufrufstack nach, wer den Aufruf durchgeführt hat.
-
Sehr gute Idee.

Also der erste Copy-Con wird von der Methode _Insert_n (vector) aufgerufen:
void _Insert_n(iterator _Where, size_type _Count, const _Ty& _Val) { // insert _Count * _Val at _Where _Ty _Tmp = _Val; // in case _Val is in sequence // HIER size_type _Capacity = capacity(); /* ... */Und der zweite von _Construct (xmemory):
// TEMPLATE FUNCTION _Construct template<class _T1, class _T2> inline void _Construct(_T1 _FARQ *_Ptr, const _T2& _Val) { // construct object at _Ptr with value _Val new ((void _FARQ *)_Ptr) _T1(_Val); // HIER }Vielleicht lässt sich das Verhalten ja aus diesem Angaben irgendwie erklären?

-
#include <vector> #include <iostream> struct A { A() { std::cout << "A()" << std::endl; } A(const A& other) { std::cout << "A(const A&)" << std::endl; } ~A() { std::cout << "~A()" << std::endl; } }; int main() { std::vector<A> vec; vec.reserve(1); // <- vec.push_back(A()); std::cin.get(); return 0; }Dadurch erhalte ich auch mit VC7.1 nur einen Copy-Konstruktor-Aufruf. Der zweite kommt daher, dass die Größe des vectors durch push_back geändert wird.
-
operator void schrieb:
[reserve]
Dadurch erhalte ich auch mit VC7.1 nur einen Copy-Konstruktor-Aufruf. Der zweite kommt daher, dass die Größe des vectors durch push_back geändert wird.Lustige Implementierung. Normalerweise sollte push_back doch zuerst gucken, ob der Vektor vergrößert werden muss, und dann das neue Element konstruieren.
-
Tatsächlich! Danke für den Hinweis.
Der zweite kommt daher, dass die Größe des vectors durch push_back geändert wird.
Jetzt frage ich mich aber wie die anderen Compiler bzw. STL Implementationen das machen. Die müssen doch auch die Vector-Kapazität vergrößern.
Ich dachte zuerst das die vielleicht schon Speicher vorreservieren, auch ohne das man das angibt, aber ein Aufruf von capacity() gibt beim g++ 0 zurück??
-
Langsam zweifle ich wirklich an MicroSoft. Dev-C++ und MSVC++ 6 toppen den neuen MSVC++ 7.1.

-
Wenn der vector vergrößert wird, muss eh für jedes bisherige Element einmal der Copy-Konstruktor aufgerufen werden (nur für das gerade hinzugefügte eben theoretisch nicht). Ist also so gesehen kein schwerer Verlust. Der Kommentar im gepasteten Code verrät doch außerdem, warum es im VC7.1 geändert wurde: "in case _Val is in sequence", d.h. vec.push_back(vec[4]) geht dadurch garantiert. Ob ein assert() hier vielleicht angebrachter gewesen wäre, steht auf einem anderen Blatt

-
Hehe, wenn ich mich recht erinnere, wurde genau das vor kurzem in comp.lang.c++.moderated diskutiert.
Ob ein assert() hier vielleicht angebrachter gewesen wäre, steht auf einem anderen Blatt

Es ist doch nicht verboten, Elemente, die schon im Vektor enthalten sind, erneut einzufügen.
-
-
Das ist der falsche Link. Ich spreche von einer Diskussion darüber, wie push_back implementiert sein muss, damit auch Elemente des Vektors sicher erneut eingefügt werden können. resize ist hier völlig irrelevant.
-
Der Kommentar im gepasteten Code verrät doch außerdem, warum es im VC7.1 geändert wurde: "in case _Val is in sequence", d.h. vec.push_back(vec[4]) geht dadurch garantiert.
Verstehe ich irgendwie nicht.

Warum muss denn für den Fall einer Sequenz eine Kopie angelegt werden? (_Ty _Tmp = _Val;)
-
biz: Angenommen, capacity() == 100, size() == 100. Mit anderen Worten, der Vektor ist voll, die nächste Einfügung verursacht also eine Neuallokation. Wir führen jetzt v.push_back(v[0]) aus: An push_back wird eine const-Referenz auf das erste Element übergeben. Wenn push_back ohne Kopie implementiert ist, passiert folgendes: capacity() wird vergrößert, indem neuer Speicher herangeschafft und der alte Inhalt kopiert wird. push_back greift jetzt auf die übergebene Referenz zu, und versucht, in einem uninitialisierten Stück Speicher (v[100]) eine Kopie des referenzierten Objekts zu konstruieren. Durch das vorhergegangene Umschaufeln ist die Referenz jedoch ungültig geworden (bzw. verweist technisch gesehen in den üblichen Implementationen auf eine zerstörte Objektleiche), und das will man normalerweise nicht.
Unter den vielen möglichen Lösungen wurde anscheinend eine gewählt, die vom übergebenen Objekt eine Sicherheitskopie anlegt. Man hätte auch die ganze Last für diesen Spezialfall auf den Benutzer der Klasse umschaufeln können. Oder testen können, ob die Referenz auf den Vektor selbst verweist.