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=:     0
    

    Experiment 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=:     0
    

    Das 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;
    }
    

Anmelden zum Antworten