vector



  • Kann bitte jemand diese unterschiedlichen Vorgänge im Zusammenhang von vector erklären:

    #include <iostream>
    
    class X
    {
    public:
      X(){std::cout<<"ctor"<<std::endl;}  
     ~X(){std::cout<<"dtor"<<std::endl;}    
      X(const X& x){std::cout<<"copycon"<<std::endl;}    
      X& operator=(const X& x){std::cout<<"op="<<std::endl; return *this;}    
    };    
    
    /***********************************************************/
    
    #include <vector>
    using namespace std;
    
    int main()
    {
      X x;
      cout << endl;  
      vector<X> v(1);
      cout << endl;
      vector<X> w(3);
      cout << endl;  
      w = v;
      cout << endl;  
      v.push_back(x);  
      cin.get();
    }
    

    ctor

    ctor
    copycon
    dtor

    ctor
    copycon
    copycon
    copycon
    dtor

    op=
    dtor
    dtor

    copycon
    copycon
    dtor

    //...
    int main()
    {
      X x;
      cout << endl;  
      vector<X> v(3);
      cout << endl;
      vector<X> w(1);
      cout << endl;  
      w = v;
      cout << endl;  
      v.push_back(x);  
      cin.get();
    }
    

    ctor

    ctor
    copycon
    copycon
    copycon
    dtor

    ctor
    copycon
    dtor

    copycon
    copycon
    copycon
    dtor

    copycon
    copycon
    copycon
    copycon
    dtor
    dtor
    dtor



  • push_back arbeitet mit copycon:

    vector<X> v;
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);
    
    ctor
    
    copycon
    

    Gelöscht werden Elemente in vector, indem deren Destruktor aufgerufen wird:

    vector<X> v;
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      vector<X> w;
      cout << endl;
      v=w;
    
    ctor
    
    copycon
    
    dtor
    

    Hier wird auch bei w=v der copycon aufgerufen:

    vector<X> v;
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      vector<X> w;
      cout << endl;
      w=v;
    
    ctor
    
    copycon
    
    copycon
    

    So kommt auch der op= ins Spiel:

    vector<X> v;
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      vector<X> w;
      cout << endl;
      w=v;
      v=w;
    
    ctor
    
    copycon
    
    copycon
    op=
    

    Bei einem mehrfachen push_back kommen copycon und dtor immer stärker ins Spiel:

    vector<X> v;
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);
    
    ctor
    
    copycon
    
    copycon
    copycon
    dtor
    
    copycon
    copycon
    copycon
    dtor
    dtor
    

    Da das ständige Umkopieren (die Wohnung ist immer um eins zu eng!) ineffizient ist, erledigt man das Speicher anfordern mittels reserve():

    vector<X> v;
      v.reserve(3);
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);
    
    ctor
    
    copycon
    
    copycon
    
    copycon
    

    Das vierte push_back kostet wieder drei ineffiziente copycon plus drei Mal dtor für den Umzug in eine größere Wohnung ;):

    vector<X> v;
      v.reserve(3);
      cout << endl;
      X x;
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);  
      cout << endl;
      v.push_back(x);
    
    ctor
    
    copycon
    
    copycon
    
    copycon
    
    copycon
    copycon
    copycon
    copycon
    dtor
    dtor
    dtor
    

    So geht das verkürzt mit vier Elementen:

    X x;
      cout << endl;
      vector<X> v(4,x);
    
    ctor
    
    copycon
    copycon
    copycon
    copycon
    


  • Übrigens sieht man das alles noch besser, wenn man die Speicherstelle ausgibt:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class X
    {
    public:
      X(){std::cout<<this<<": "<<"ctor"<<std::endl;}  
     ~X(){std::cout<<this<<": "<<"dtor"<<std::endl;}    
      X(const X& x){std::cout<<this<<": "<<"copycon"<<std::endl;}    
      X& operator=(const X& x){std::cout<<this<<": "<<"op="<<std::endl; return *this;}    
    };    
    
    int main()
    {
      vector<X> v(4);
      cin.get();
    }
    
    0x22ff38: ctor
    0x3f2500: copycon
    0x3f2501: copycon
    0x3f2502: copycon
    0x3f2503: copycon
    0x22ff38: dtor
    

    Hier sieht man, dass ein Objekt vom Typ X auf dem Stack erzeugt wird (ctor). Anschließend wird es vier Mal (copycon) in den freestore kopiert und das Objekt auf dem Stack wieder zerstört (dtor).
    Das steht sogar falsch im Josuttis (S.149):

    std::vector<T> v(5) // calls five times the default constructor of type T



  • Nun zur Ausgangsfrage. Wir verwenden eine Klasse X, die uns beim copycon und op= auch die Herkunft verrät:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class X
    {
    public:
      X(){std::cout<<this<<": "<<"ctor"<<std::endl;}  
     ~X(){std::cout<<this<<": "<<"dtor"<<std::endl;}    
      X(const X& x){std::cout << this << ": " << "copycon von " << &x << std::endl;}    
      X& operator=(const X& x){std::cout << this << ": " << "op= von " << &x << std::endl; return *this;}    
    };    
    
    int main()
    {
      X x;
      cout << endl;  
      vector<X> v(1);
      cout << endl;
      vector<X> w(3);
      cout << endl;  
      w = v;
      cout << endl;  
      v.push_back(x);  
      cin.get();
    }
    
    0x22ff58: ctor
    
    0x22ff28: ctor
    0x3f2528: copycon von 0x22ff28
    0x22ff28: dtor
    
    0x22ff28: ctor
    0x3f2530: copycon von 0x22ff28
    0x3f2531: copycon von 0x22ff28
    0x3f2532: copycon von 0x22ff28
    0x22ff28: dtor
    
    0x3f2530: op= von 0x3f2528
    0x3f2531: dtor
    0x3f2532: dtor
    
    0x3f2538: copycon von 0x3f2528
    0x3f2539: copycon von 0x22ff58
    0x3f2528: dtor
    

    Zweiter Fall:

    X x;
      cout << endl;  
      vector<X> v(3);
      cout << endl;
      vector<X> w(1);
      cout << endl;  
      w = v;
      cout << endl;  
      v.push_back(x);
    
    0x22ff58: ctor
    
    0x22ff28: ctor
    0x3f2528: copycon von 0x22ff28
    0x3f2529: copycon von 0x22ff28
    0x3f252a: copycon von 0x22ff28
    0x22ff28: dtor
    
    0x22ff28: ctor
    0x3f2530: copycon von 0x22ff28
    0x22ff28: dtor
    
    0x3f2538: copycon von 0x3f2528
    0x3f2539: copycon von 0x3f2529
    0x3f253a: copycon von 0x3f252a
    0x3f2530: dtor
    
    0x3f2530: copycon von 0x3f2528
    0x3f2531: copycon von 0x3f2529
    0x3f2532: copycon von 0x3f252a
    0x3f2533: copycon von 0x22ff58
    0x3f2528: dtor
    0x3f2529: dtor
    0x3f252a: dtor
    

    Jetzt liegt alles offen auf dem Tisch. 😉

    Noch ein anderes Beispiel:

    vector<X> v(3);
     cout << endl;  
     list<X>l(3);
     cout << endl;
     v.assign(l.begin(),l.end());
    
    0x22ff38: ctor
    0x3f2500: copycon von 0x22ff38
    0x3f2501: copycon von 0x22ff38
    0x3f2502: copycon von 0x22ff38
    0x22ff38: dtor
    
    0x22ff38: ctor
    0x3f25b8: copycon von 0x22ff38
    0x3f25c8: copycon von 0x22ff38
    0x3f25d8: copycon von 0x22ff38
    0x22ff38: dtor
    
    0x3f2500: op= von 0x3f25b8
    0x3f2501: op= von 0x3f25c8
    0x3f2502: op= von 0x3f25d8
    

Anmelden zum Antworten