Beiträge von crib

    Noch was:


    Schreibe die Klasse 'test' mit einem default ctor, einem copy ctor und einem assignment operator und schau, was folgender Code macht:


    mymap[1];


    Dann geht dir vielleicht ein Licht auf. ;)

    Zitat

    So ganz stimmt das immer noch nicht, die rechte Seite ist nicht test() sondern *(new test()) und damit ein temporary...


    Der Ausdruck "*(new test())" ist in sich bereits ein memleak und damit ein schwerer Fehler. Was du mit new allokierst, musst du auch wieder frei geben.
    Aber selbst wenn du es wieder frei geben würdest, es wäre trotzdem schlechter Code. Das temporäre Objekt gehört auf den Stack, wie in meinem Beispiel!


    Zitat

    Naja, hier war ich wohl in Gedanken bei std::vector<>::push_back(). Aber bis auf die Kopie beim Einfügen stimmt's schon...


    Nein tut es nicht.


    Zitat

    Das sehe ich ja genauso. Aber woher kommt der dritte Destruktor-Aufruf? Egal wie ich es drehe, ich sehe bei dem Code abgesehen vom mit new konstruierten Objekt nur zwei weitere Objekte.


    Insgesamt werden VIER Objekte erstellt und damit VIER Konstruktoren und VIER Destruktoren ausgeführt!


    Im Detail:


    Wir gehen aus von:
    std::map<int, test> mymap;
    mymap[1] = test();


    mymap[1] führt zu:


    neues objekt wird erstellt (1)
    kopie von (1) wird erstellt (2)
    kopie von (2) wird erstellt (3)
    (1) wird zerstoert
    (2) wird zerstoert
    (3) befindet sich jetzt in der map


    bis hierher ist es das interne Verhalten von std::map<>


    jetzt kommt unsere Zuweisung: ' = test()'


    temporaeres objekt wird erstellt (4)
    der assignment-operator von (3) wird mit (4) als parameter aufgerufen
    (4) wird zerstoert


    Am Ende des Programms wird mymap zerstört und damit alle Destruktoren der enthaltenen Objekte aufgerufen, dies führt zu:


    (3) wird zerstört


    ----------------------------------------


    Dieses elementare Verhalten zu verstehen ist ungemein wichtig, wenn man C++ programmieren will. Insbesondere den Code, den der C++ Compiler selber erzeugt, ohne dass man davon weiss (copy ctor, assigment operator, ...).

    Zitat

    Originally posted by zuse
    [...]
    Mir ist vollkommen unklar wieso beim Einfügen der Instanz von 'test' der Konstruktor zweimal aufgerufen wird. Die Map ist doch eigentlich nur ein Container, der den Inhalt eigentlich nicht anfassen sollte.
    [...]


    <stdlib.h> ersetzt du bitte durch <iostream>
    und
    printf("whatever"); ersetzt du bitte durch std::cout << "whatever" << std::endl;


    ausgehend von std::map<int, test>:


    m_map[1] = *(new test()); <--- falsch + gefaehrlich


    m_map[1] = test(); <--- richtig


    Das von dir bebachtete Verhalten sind fundamentale Eigenschaften von C++.
    Da du keinen copy ctor definiert hast, erstellt der C++ Compiler für dich einen default copy ctor. Von deinem erstellten Objekt werden bei verschiedenen Assignment-Operationen kurzfristige, für dich nicht ersichtliche Kopien erstellt. Dies sind aber ganz normale Instanzen deiner Klasse, dessen dtor nach der Verwendung ausgeführt wird. Genau dieses Verhalten hast du beobachtet!


    Wichtig ist hier auch:
    m_map[1] erstellt zuerst! ein neues Objekt vom Typ 'test', fügt es in die map ein und gibt dann eine Referenz auf das neue Objekt zurück. Im nächsten Schritt wird der Assignment-Operator operator=(const test& rhs) dieses Objekts aufgerufen mit test() als Argument. Das Argument wird ebenfalls nach der Assignment-Operation zerstört.



    Btw. wenn du mit new arbeitest, schau dir vorher std::auto_ptr<> bzw. boost::shared_ptr<> und boost::weak_ptr<> an!



    Zitat

    Originally posted by zirias
    Dass es 3 Kopien sind, ist leicht erklärt. Die erste erstellst du mit dem new-Operator. Die zweite ist ein sogenanntes temporary object: da die rechte Seite deiner Zuweisung ein Ausdruck ist (dereferenzierung von irgendwas) wird hier ein temporäres Objekt mit dem Ergebnis des Ausdrucks erstellt, dessen Lebensdauer sich auf die eine Anweisung beschränkt. Für die dritte Kopie ist dann std::map<> verantwortlich, denn das ist, wie du schon sagst, ein Container. Wenn es Objekte einer Klasse speichern soll tut es auch genau das (und speichert nicht etwa Pointer oder Referenzen), also muss es das Objekt kopieren, das gespeichert werden soll.


    Das stimmt so leider nicht.



    Zitat

    Originally posted by zirias
    Was mich bei deiner Ausgabe wundert ist, dass der Destruktor der ersten Kopie überhaupt aufgerufen wird. Da du den Rückgabewert von new nicht speicherst sondern direkt weiterverarbeitest (in dem Fall mit *), ist der Pointer zu deiner ersten Kopie eigentlich verloren, das sollte normalerweise ein Speicherleck sein.


    Der Destruktor des durch new() instantiierten Objekts wird in diesem Beispiel nie ausgefuehrt.