obj X in vextor in vector
-
Wenn ich ein Objekt X in einem vector habe und diesen vector wieder in einen vector setze und anschließend alles destuiere, was kommt zuerst? innerer oder äußerer vector? und warum? wie kann man das beweisen?
-
Ein vector ruft in seinem Destruktor immer erst die Destruktoren der in ihm enthaltenden Elemente auf. Das sind in diesem Fall wieder vector-Instanzen. etc.
Wenn du es genau wissen willst, schau doch einfach in den Quelltext vom Destruktor von vector deiner STL-Bibliothek.
-
Das machen wir zusätzlich experimentell mit einer Testklasse, die Aktionen ihres Innenlebens loggt (Testklasse siehe: http://www.henkessoft.de/C++/C++ Fortgeschrittene/C++_Fortgeschrittene.htm#2.2._Unsere_Testklasse_loggt_mit ):
//Testklasse #define _TEST_ #include <windows.h> #include <conio.h> #include <iostream> #include <vector> using namespace std; void textcolor(WORD color) { SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), color); } const int farbe1 = 3; const int farbe2 = 15; class xINT { private: int num; static int countCtor; static int countDtor; static int countCopycon; static int countOpAssign; public: xINT() { #ifdef _TEST_ textcolor(farbe1); std::cout << this << ": " << "ctor" << std::endl; textcolor(farbe2); #endif ++countCtor; } ~xINT() { #ifdef _TEST_ textcolor(farbe1); std::cout << this << ": " << "dtor" << std::endl; textcolor(farbe2); #endif ++countDtor; } xINT(const xINT& x) { #ifdef _TEST_ textcolor(farbe1); std::cout << this << ": " << "copycon von " << std::dec << &x << std::endl; textcolor(farbe2); #endif num = x.getNum(); ++countCopycon; } xINT& operator=(const xINT& x) { if (&x == this) { #ifdef _TEST_ textcolor(farbe1); std::cout << "Selbstzuweisung mit op=" << std::endl; textcolor(farbe2); #endif } #ifdef _TEST_ textcolor(farbe1); std::cout << this << ": " << "op= von " << std::dec << &x << std::endl; textcolor(farbe2); #endif num = x.getNum(); ++countOpAssign; return *this; } int getNum() const {return num;} void setNum(int val) {num = val;} static void statistik(std::ostream&); static void reset(); }; int xINT::countCtor = 0; int xINT::countDtor = 0; int xINT::countCopycon = 0; int xINT::countOpAssign = 0; void xINT::statistik(std::ostream& os) { textcolor(farbe1); os << "Ctor: " << countCtor << std::endl << "Dtor: " << countDtor << std::endl << "Copycon: " << countCopycon << std::endl << "op=: " << countOpAssign; textcolor(farbe2); } void xINT::reset() { countCtor = 0; countDtor = 0; countCopycon = 0; countOpAssign = 0; } std::ostream& operator<< (std::ostream& os, const xINT& x) { os << x.getNum(); return os; } std::istream& operator>> (std::istream& is, xINT& x) { int i; is >> i; x.setNum(i); return is; } bool operator< (const xINT& a, const xINT& b) { return a.getNum() < b.getNum(); } bool operator> (const xINT& a, const xINT& b) { return a.getNum() > b.getNum(); } bool operator== (const xINT& a, const xINT& b) { return a.getNum() == b.getNum(); } bool operator!= (const xINT& a, const xINT& b) { return a.getNum() != b.getNum(); } /***********************************************************/ // Klient für Testklasse int main() { { cout << "xINT konstruieren\n" << endl; xINT x; //ctor der Testklasse xINT in Aktion xINT::statistik(cout); xINT::reset(); getch(); vector<xINT> ct; //Container const int N = 10; ct.reserve(N); //Wichtig für die Effizienz! cout << "\n\nct.push_back(x)\n" << endl; for(int i=0; i<N; ++i) { x.setNum(i); ct.push_back(x); //copycon der Testklasse xINT in Aktion } xINT::statistik(cout); xINT::reset(); getch(); cout << "\n\nct1.push_back(ct);\n" << endl; vector< vector<xINT> > ct1; //Container ct1.reserve(1); ct1.push_back(ct); xINT::statistik(cout); xINT::reset(); getch(); cout << "\n\n} bewirkt dtor\n" << endl; } xINT::statistik(cout); xINT::reset(); getch(); return 0; } output auf cout: [code] xINT konstruieren 0x22ff50: ctor Ctor: 1 Dtor: 0 Copycon: 0 op=: 0 ct.push_back(x) 0x3f3ec0: copycon von 0x22ff50 0x3f3ec4: copycon von 0x22ff50 0x3f3ec8: copycon von 0x22ff50 0x3f3ecc: copycon von 0x22ff50 0x3f3ed0: copycon von 0x22ff50 0x3f3ed4: copycon von 0x22ff50 0x3f3ed8: copycon von 0x22ff50 0x3f3edc: copycon von 0x22ff50 0x3f3ee0: copycon von 0x22ff50 0x3f3ee4: copycon von 0x22ff50 Ctor: 0 Dtor: 0 Copycon: 10 op=: 0 ct1.push_back(ct); 0x3f3ef8: copycon von 0x3f3ec0 0x3f3efc: copycon von 0x3f3ec4 0x3f3f00: copycon von 0x3f3ec8 0x3f3f04: copycon von 0x3f3ecc 0x3f3f08: copycon von 0x3f3ed0 0x3f3f0c: copycon von 0x3f3ed4 0x3f3f10: copycon von 0x3f3ed8 0x3f3f14: copycon von 0x3f3edc 0x3f3f18: copycon von 0x3f3ee0 0x3f3f1c: copycon von 0x3f3ee4 Ctor: 0 Dtor: 0 Copycon: 10 op=: 0 } bewirkt dtor 0x3f3ef8: dtor <--- Elemente xINT erzeugt durch vector< vector<xINT> > ct1 0x3f3efc: dtor werden abgebaut 0x3f3f00: dtor 0x3f3f04: dtor 0x3f3f08: dtor 0x3f3f0c: dtor 0x3f3f10: dtor 0x3f3f14: dtor 0x3f3f18: dtor 0x3f3f1c: dtor 0x3f3ec0: dtor <--- Ursprüngliche Elemente von vector<xINT> ct 0x3f3ec4: dtor kommen anschließend an die Reihe 0x3f3ec8: dtor 0x3f3ecc: dtor 0x3f3ed0: dtor 0x3f3ed4: dtor 0x3f3ed8: dtor 0x3f3edc: dtor 0x3f3ee0: dtor 0x3f3ee4: dtor 0x22ff50: dtor <--- xINT auf dem Stack Ctor: 0 Dtor: 21 Copycon: 0 op=: 0[/code] }Man sieht, die Realität ist irgendwie komplexer, als das oben beschrieben dargestellt wird. Die Preisfrage ist: Wo auf dem Heap befinden sich zu welchem Zeitpunkt eigentlich die echten 10 Elemente des Vector ct?

-
Wir verändern den Versuchsaufbau auf zwei Arten, indem wir einmal die Elemente des inneren vector mit ct.clear() und ein anderes Mal die Elemente des äußeren vector mit ct1.clear() "zerstörend" aufrufen:
Experiment 1 (innerer vector):
//... cout << "\n\nct.clear() bewirkt dtor\n" << endl; ct.clear(); xINT::statistik(cout); xINT::reset(); getch(); cout << "\n\n} bewirkt dtor\n" << endl; } xINT::statistik(cout); xINT::reset(); getch(); return 0; }output:
ct.clear() bewirkt dtor 0x3f3ec0: dtor 0x3f3ec4: dtor 0x3f3ec8: dtor 0x3f3ecc: dtor 0x3f3ed0: dtor 0x3f3ed4: dtor 0x3f3ed8: dtor 0x3f3edc: dtor 0x3f3ee0: dtor 0x3f3ee4: dtor Ctor: 0 Dtor: 10 Copycon: 0 op=: 0 } bewirkt dtor 0x3f3ef8: dtor 0x3f3efc: dtor 0x3f3f00: dtor 0x3f3f04: dtor 0x3f3f08: dtor 0x3f3f0c: dtor 0x3f3f10: dtor 0x3f3f14: dtor 0x3f3f18: dtor 0x3f3f1c: dtor 0x22ff50: dtor Ctor: 0 Dtor: 11 Copycon: 0 op=: 0Experiment 2 (äußerer vector):
//... cout << "\n\nct1.clear() bewirkt dtor\n" << endl; ct1.clear(); xINT::statistik(cout); xINT::reset(); getch(); cout << "\n\n} bewirkt dtor\n" << endl; } xINT::statistik(cout); xINT::reset(); getch(); return 0; }output:
ct1.clear() bewirkt dtor 0x3f3ef8: dtor 0x3f3efc: dtor 0x3f3f00: dtor 0x3f3f04: dtor 0x3f3f08: dtor 0x3f3f0c: dtor 0x3f3f10: dtor 0x3f3f14: dtor 0x3f3f18: dtor 0x3f3f1c: dtor Ctor: 0 Dtor: 10 Copycon: 0 op=: 0 } bewirkt dtor 0x3f3ec0: dtor 0x3f3ec4: dtor 0x3f3ec8: dtor 0x3f3ecc: dtor 0x3f3ed0: dtor 0x3f3ed4: dtor 0x3f3ed8: dtor 0x3f3edc: dtor 0x3f3ee0: dtor 0x3f3ee4: dtor 0x22ff50: dtor Ctor: 0 Dtor: 11 Copycon: 0 op=: 0Das Ergebnis ist unterschiedlich. Wie steht es jetzt mit der obigen Aussage? Wo sind die echten Elemente von ct?

-
Das ist ja interessant. Der äußere vector erzeugt offenbar "Doppelgänger", die er bei seinem clear() oder seinem Ableben auch zerstört. Die Originale des inneren vectors überleben offensichtlich den Tod des äußeren vectors und seiner Doppelgänger, müssen dann aber auch kollektiven Selbstmord begehen.

Wozu sind diese Doppelgänger eigentlich da? Wer greift wann auf diese zu?
Rattenscharf!
-
Wozu sind diese Doppelgänger eigentlich da? Wer greift wann auf diese zu?
Beim ersten vector wurde x (vom Typ xINT) ebenfalls jeweils vom Stack zum Heap "kopierend konstruiert" (copycon). Das läuft beim zweiten vector gleich. STL bedeutet: "copy in, copy out" (Scott Meyers). Man hat es ständig mit "Doppelgängern" zu schaffen.

Ansonsten müsste man mit Zeigern/Referenzen arbeiten.
Die Originale des inneren vectors überleben offensichtlich den Tod des äußeren vectors und seiner Doppelgänger, müssen dann aber auch kollektiven Selbstmord begehen.
Wozu sind diese Doppelgänger eigentlich da? Wer greift wann auf diese zu?Den Originalen ist es egal, ob ihre "Doppelgänger" gekillt werden. Wie gesagt, STL liebt den copycon.

-
Destruktor von vector:
~vector() { // destroy the object _Tidy(); } void _Tidy() { // free all storage if (_Myfirst != 0) { // something to free, destroy and deallocate it _Destroy(_Myfirst, _Mylast); this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst); } _Myfirst = 0, _Mylast = 0, _Myend = 0; }