Anfängerfrage, warum wird deconstructor so oft aufgerufen



  • Hallo zusammen

    Ich habe eine simple Klasse "Person", welche den Namen der Person speichert und beim Aufruf des Deconstructors jeweils eine Ausgabe "deconstructor of Personname called". Hier des Beispielcode in abgekürzter Form:

    //Create Persons on Stack
        Person John{"John", 32};
        Person Marc{"Marc", 58};
        Person Rene{"Rene", 45};
    
    //Create a Tresor-Obj on Stack
        Tresor StackTresor{};
    
    // Call method of Tresor which stores Persons in a vector
        StackTresor.storePerson(John);
        StackTresor.storePersonBySingleRef(Marc);
        StackTresor.storePersonByReference(Rene);
    

    und hier die Methodenimplementation

    void Tresor::storePerson(Person person)
    {
        myPersons.push_back(person);
    }
    
    void Tresor::storePersonBySingleRef(Person &person)
    {
        myPersons.push_back(person);
    }
    
    void Tresor::storePersonByReference(Person &person)
    {
        myPersonsPtr.push_back(&person);
    }
    

    Ich erhalte folgende Ausgabe in der Konsole

    constructor of John called
    constructor of Marc called
    constructor of Rene called
    virtual deconstructor of John called
    virtual deconstructor of John called
    virtual deconstructor of John called
    virtual deconstructor of Marc called
    virtual deconstructor of Rene called
    virtual deconstructor of Marc called
    virtual deconstructor of John called

    Frage: Warum wird der Deconstructor of John 4mal aufgerufen? Ich hätte dreimal erwartet.

    Weil:

    //Create Person on Stack
    Person John{} 1x

    StackTresor.storePerson(John) 2x
    {
    vector.push-back(John) 3x
    }


  • Mod

    Du machst versteckte Kopien beim Einfügen in den Vector. Naja, so versteckt sind sie auch nicht, aber in deiner Ausgabe sind sie halt nicht sichtbar 🙂

    Implementiere auch noch einen Kopierkonstruktor mit einer Ausgabe, dann siehst du auch das Erstellen dieser Kopien. Wenn du gründlich sein möchtest, auch noch den Zuweisungsoperator (wobei der aber keine neuen Objekte erzeugt, und zumindest in deinem derzeitigen Programm auch nicht genutzt wird). Damit kannst du das Leben eines Objekts vollständig verfolgen.



  • ...und auch ein vector.push_back kann je nach capacity und size dafür sorgen, dass die bislang existierenden Objekte in neuen Speicherbereich umkopiert werden müssen - nämlich wenn die neue Größe zu groß wird (es werden deine Objekte auch gemovet, wenn dein Move-Konstruktor noexcept ist) Dadurch kannst du also auch einen Destruktorcall bekommen.



  • Danke euch beiden für die Antworten. Dann werde ich einmal versuchen den Kopierkonstruktor mit einer Ausgabe zu implementieren und führe den Test nochmals durch. Danke auch für die Info, dass die Vector-Klasse "selbständig" unter gewissen Bedingungen einen Move/Copy durchführt 🙂



  • Deconstructor

    Es heisst zwar "Destruktor", aber "Dekonstruktor" trifft es eigentlich auch ziemlich gut. Schliesslich sollten kompexere Objekte nicht einfach gespengt, sondern fein säuberlich demontiert werden 🙂





  • Ich habe jetzt noch den Kopierkonstruktor mit einer Ausgabe der Klasse Person hinzugefügt. Hier der Output:

    constructor of John called -> wegen Person John{}
    Copy constructor of John called -> wegen StackTresor.storePerson(John)
    Copy constructor of John called -> wegen vector.push-back(John)
    virtual deconstructor of John called -> ?
    Copy constructor of John called -> ?
    virtual deconstructor of John called
    virtual deconstructor of John called
    virtual deconstructor of John called

    Alles nach -> habe ich eingefügt. Ich verstehe nicht was bei ? passiert 😞


  • Mod

    Du hast deinen Beitrag zwar gelöscht, aber ich kann die Frage vielleicht trotzdem für andere Leser beantworten:

    void Tresor::storePerson(Person person)
    {
        myPersons.push_back(person);
    }
    

    Das person in dieser Funktion ist ebenfalls eine Kopie und dieses Objekt wird beim Verlassen der Funktion zerstört. Das soll dein Programm ja vermutlich auch zeigen, da das bei storePersonBySingleRef nicht passiert.



  • @SeppJ Danke dir, macht Sinn 😀



  • Ich habe noch eine Anschlussfrage:

       //Create Persons on the Heap
        auto Rambo = new Person("Rambo", 44);
        //auto McClain = new Person("McClain", 39);
        auto Connor = new Person("Connor", 23);
    
        //Create a Tresor-Obj on Stack
        Tresor StackTresor{};
    
        StackTresor.storePerson(Rambo); //Person*
        StackTresor.storePersonByCopyByReference(Connor); //Connor*
    

    und hier die Methodenimplementation:

    void Tresor::storePerson(Person *person)
    {
        myPersonsPtr.push_back(person);
    }
    
    void Tresor::storePersonByCopyByReference(Person *person)
    {
        myPersonsPtr.push_back(&*person);
    }
    

    Frage: Ist es richtig, dass beide Methoden genau das gleich machen. Nämlich sie speichern einen Pointer zu einem Person-Obj in einem Vector<Person*>.


  • Mod

    &* hebt sich gegenseitig auf, also ist person dasselbe wie &*person.

    (Da sind übrigens nirgends Kopien oder Referenzen in deinem storePersonByCopyByReference)



  • @SeppJ Super, danke dir :). Ja, meine Methodennamen sind komplett irreführend und falsch, da hast du recht.


Anmelden zum Antworten