Destruktor und Heap?



  • Hallo,

    ich habe ein Klasse die etwa so aussieht:

    class Test
    {
      public:
        Test()
        {
            this->testVar = new char[200];
        };
        virtual blub(){};
    
      private:
        char* testVar;
    };
    

    Mir stellen sich jetzt 2 Fragen:
    1. Muss ich im Destruktor delete this->testVar aufrufen?
    2. Wie sieht das mit dem virtuellem Destruktor aus? Wie wird da die auf dem Heap erzeugter char[200] behandelt?

    Danke!


  • Mod

    psih schrieb:

    1. Muss ich im Destruktor delete this->testVar aufrufen?

    ja, es sei denn, du weißt, dass du es nicht tun musst. Allerdings mit dem richtigen Operator:

    delete [b][][/b] this->testVar
    

    psih schrieb:

    Wie sieht das mit dem virtuellem Destruktor aus? Wie wird da die auf dem Heap erzeugter char[200] behandelt?

    nicht anders.



  • 1.) Der den Speicher verwaltet, muss ihn freigeben (ownership etc.). Kurz: Ja.
    2.) Ist der Destruktor virtuell, so wird von C++ garantiert, das jeder gemaess der Klassenhierarchie abgearbeitet wird (wie beim Auspacken von Geschenken).

    Zu 1.): Genauer: kommt drauf an ...



  • Also wird der virtuelle Destruktor garnicht überschrieben?

    MfG



  • Ja, du solltest den Speicher wieder freigeben. Ausserdem wäre es gut, du würdest Kopierkonstruktor und Zuweisungsoperator implementieren (oder verbieten), sonst hast du schnell Probleme.

    class Test
    {
        public:
            // Konstruktor: Grundsätzlich Initialisierungsliste verwenden
            Test()
            : testVar(new char[200]) // <- das hier, Member per Komma trennen
            {
            } // kein Semikolon nötig hier
    
            // Destruktor: Speicher freigeben
            ~Test()
            {
                delete[] testVar;
            }
    
            // Kopierkonstruktor: Neuen Speicher anfordern und Inhalt kopieren
            Test(const Test& Origin)
            : testVar(new char[200])
            {
                std::copy(Origin.testVar, Origin.testVar+200, testVar);
            }
    
            // Zuweisungsoperator: Alten Speicher freigeben, neuen anfordern, kopieren
            Test& operator= (const Test& Origin)
            {
                delete[] testVar;
                testVar = new char[200];
                std::copy(Origin.testVar, Origin.testVar+200, testVar);
                return *this;
            }
    
        private:
            char* testVar;
    };
    

    Ich lege dir nahe, etwas Literatur darüber zu lesen, das ist nämlich sehr wichtig. Am Rande sei auch noch das Copy&Swap-Idiom erwähnt, um sicherer zuzuweisen.

    Im Normalfall solltest du eher auf die STL-Container und std::string zurückgreifen, als Speicherverwaltung selbst handzuhaben. Im Magazin dieses Forums gibt es ein paar Artikel über die STL, schau dir die vielleicht mal an. Diese ist nämlich ebenfalls ein sehr elementarer Bestandteil von C++.


  • Mod

    Nexus schrieb:

    Test& operator= (const Test& Origin)
            {
                delete[] testVar;
                testVar = new char[200];
                std::copy(Origin.testVar, Origin.testVar+200, testVar);
            }
    

    so schon mal nicht.

    Test& operator= (const Test& Origin)
            {
                char* p = new char[200];
                std::copy(Origin.testVar, Origin.testVar+200, p);
    
                delete[] testVar;
                testVar = p;
                return *this;
            }
    

    Befindet sich ein delete am Anfang der Funktion, ist in 99% der Fälle ein Fehler drin: es ist einerseits nicht exception-sicher und zudem klappt hier auch die Selbstzuweisung nicht.



  • camper schrieb:

    so schon mal nicht.

    Was meinst du? Copy&Swap habe ich erwähnt, das fehlende return *this; war ein Flüchtigkeitsfehler, der auch schon behoben ist.



  • Ich würde hier sagen, dass du leicht eine höhere Exceptionsicherheit geben kannst, und darum hätte ich da entweder gleich Copy/Swap implementiert oder es als Kommentar da reingeschrieben, als Aufgabe für den TO. 😉

    EDIT:
    Weil es halt so verlockend ist etwas zu kopieren, dass läuft, ohne zu überlegen..



  • camper schrieb:

    Befindet sich ein delete am Anfang der Funktion, ist in 99% der Fälle ein Fehler drin: es ist einerseits nicht exception-sicher und zudem klappt hier auch die Selbstzuweisung nicht.

    Exceptionsicherheit bei char ? Ok, std::bad_alloc . Aber angesichts der Tatsache, dass 1. psih wohl schon mit dem normalen Code viel zu lernen hat, 2. ich auf Copy&Swap hingewiesen habe und 3. bad_alloc bei einer Anforderung von 200 char s wohl eher nicht auftritt (Selbstzuweisung ist auch recht selten), habe ich es halt einfach gelöst. Gut, wäre besser gewesen, es gleich richtig zu machen. Ich wollte psih halt nicht überfordern. Bin ich deswegen ein schlechter Mensch? 😉

    Bevor man sich um Exceptionsicherheit und ähnliche Dinge kümmert, sollte man meiner Ansicht nach die Grundlagen - Kopiersemantik, Speicherverwaltung und solche Sachen - verstanden haben. Ok, das mit der Selbstzuweisung kann wirklich blöd laufen. Hab ich zuerst gar nicht mehr dran gedacht, da ich eigentlich immer Copy&Swap verwende... Sorry.

    drakon schrieb:

    Ich würde hier sagen, dass du leicht eine höhere Exceptionsicherheit geben kannst, und darum hätte ich da entweder gleich Copy/Swap implementiert oder es als Kommentar da reingeschrieben, als Aufgabe für den TO. 😉

    Steht doch extra da, weil ich solche Antworten befürchtet habe! :p



  • Nexus schrieb:

    drakon schrieb:

    Ich würde hier sagen, dass du leicht eine höhere Exceptionsicherheit geben kannst, und darum hätte ich da entweder gleich Copy/Swap implementiert oder es als Kommentar da reingeschrieben, als Aufgabe für den TO. 😉

    Steht doch extra da, weil ich solche Antworten befürchtet habe! :p

    Jup. Das habe ich schon gesehen. Aber ich hätte halt einfach mal eben Copy/Swap gemacht, weil das halt doch recht intuitiv ist und er dann gar nicht auf die Idee kommt im Zuweisungsoperator sonst irgendwelche krumme Sachen zu drehen. 😉

    Ich find halt, dass dieses Idiom vor allem für Anfänger doch recht gelegen kommt, weil er dann merkt, dass er sich da überhaupt keine speziellen Gedanken machen muss, sondern eben ganz einfach dem Schema F folgen und das implementieren (auch für andere Klassen).



  • Hm ja, eigentlich hast du schon Recht. Nächstes Mal versuche ich vielleicht besser, gleich eine gute Lösung zu bringen.

    Naja, dann wird es eben wieder andere Leute geben, die mich des Overengeneerings beschuldigen. 🤡



  • Nexus schrieb:

    Naja, dann wird es eben wieder andere Leute geben, die mich des Overengeneerings beschuldigen. 🤡

    Darum hätte ich mich mit einem schlichten:

    Test& operator= (const Test& Origin)
            {
              //copy/swap
            }
    

    begnügt. 😉


Anmelden zum Antworten