Destruktoren richtig einsetzen



  • skluge schrieb:

    werden dann mit
    "delete objekt" gleichzeitig die Komponenten auch deleted...

    Solange diese innerhalb der Klasse erzeugten Objekte im Destruktor der Klasse wieder 'deleted' werden: Ja.
    Es geschieht also implizit, da durch das delete der Destruktor aufgerufen wird.
    Das kannst Du (fast) beliebig tief schachteln.

    [Edit] Oder meinst Du Klassen, die von anderen Klassen abgeleitet sind? Aber auch hier: Ja. Der Destruktor der Basisklasse wird ebenfalls durchlaufen, wenn der Destruktur der abgeleiteten Klasse aufgerufen wird.



  • Hallo

    allerdings ist dein Codebeispiel zum Beispiel Beispiel für ein Speicherloch. Denn der Speicher von

    c_Klasse *cklasse = new c_Klasse();

    wird niht wieder freigegeben, und auch der Destruktor davon wird nicht aufgerufen.
    Zum einen solltest du aus der globalen Instanz eine Memberinstanz des Forms machen. Denn dann kannst du std::auto_ptr verwenden, um automatisch mit beenden des Forms auch die Instanz löschen zu lassen, ohne extr noch den Destruktor des Forms schreiben zu müssen.

    bis bald
    akari



  • Eek, das hatte ich völlig übesehen, akari hat natürlich völlig recht.

    Globale Variablen sind böse. Siehe auch den FAQ-Beitrag dazu.



  • Danke schon mal für eure Antwort;

    Meint ihr das dann vielleicht so:

    Test.cpp:

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Test.h"
    
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
    {
    
       cklasse = new c_Klasse();
    
       cklasse->macheWas();
    
       AnsiString asText = cklasse->m_pStringList->Text;
    
       delete cklasse;
    
       exit(0);
    
    }
    //---------------------------------------------------------------------------
    

    Test.h:

    //---------------------------------------------------------------------------
    
    #ifndef TestH
    #define TestH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include "Klasse.h"
    //------
    
    //---------------------------------------------------------------------------
    class TForm1 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
       TButton *Button1;
    private:	// Anwender-Deklarationen
    public:		// Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
    
       c_Klasse *cklasse;
    
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    

    Klasse.cpp

    //---------------------------------------------------------------------------
    
    #pragma hdrstop
    
    #include "Klasse.h"
    
    //---------------------------------------------------------------------------
    
    c_Klasse::c_Klasse()
    {
       m_pSMTP = new TNMSMTP(Application);
       m_pStringList = new TStringList();
    
    }
    
    void c_Klasse::macheWas()
    {
       m_pStringList->Add("Das ist ein Test");
       m_pStringList->Add("Ende.");
    }
    
    c_Klasse::~c_Klasse()
    {
       delete m_pSMTP;
       delete m_pStringList;
    }
    

    Klasse.h:

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #include "NMsmtp.hpp"
    #ifndef KlasseH
    #define KlasseH
    
    class c_Klasse
    {
       private:
    
       TNMSMTP   *m_pSMTP;
    
       public:
    
       c_Klasse();
       ~c_Klasse();
    
       TStringList    *m_pStringList;
    
       void macheWas();
    
    };
    //---------------------------------------------------------------------------
    #endif
    

    @akari:

    Denn dann kannst du std::auto_ptr verwenden...

    Was meinst du damit? Kannst du mal ein kleines Beispiel geben wie man das anwendet?

    Wenn ich jetzt das Programm wie oben hätte, würden dann noch speicherlöcher entstehen?

    Gruß



  • Hallo

    dein neues Beispiel hat nun kein Speicherloch mehr, allerdings ist so natürlich die Membervariable sinnfrei, weil si ja nur lokal verwendet wird.

    Das mit dem auto_ptr sieht dann so aus:

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Test.h"
    
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner), cklasse(new c_Klasse()) // Initialisierungsliste mit Ini. des  auto_ptr
    {
    
       cklasse->macheWas();
    
       AnsiString asText = cklasse->m_pStringList->Text;
    
       exit(0);
    
    }
    //---------------------------------------------------------------------------
    
    //---------------------------------------------------------------------------
    
    #ifndef TestH
    #define TestH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include <memory> // Include für auto_ptr
    #include "Klasse.h"
    //------
    
    //---------------------------------------------------------------------------
    class TForm1 : public TForm
    {
    __published:    // Von der IDE verwaltete Komponenten
       TButton *Button1;
    private:    // Anwender-Deklarationen
    public:        // Anwender-Deklarationen
       __fastcall TForm1(TComponent* Owner);
    
       std::auto_ptr<c_Klasse> cklasse; // Deklaration des auto_ptr
    
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    

    So kannst du 100% sicher sein, das mit dem Löschen jeder Instanz der Form (also auch beim Beenden des Programms) auch die jeweilige Instanz von c_KLasse gelöscht wird.

    bis bald
    akari



  • Noch was zu exit(). Bei Aufruf von exit wird das Programm beendet und es werden keine Destruktoren aufgerufen. Hier sollte eher Close() oder im Notfall Application->Terminate() verwendet werden.
    @akari
    Das mit dem exit im Konstruktor meinst du doch hoffentlich nicht ernst.



  • Hallo

    Das exit stammt aus dem Quellcode von skluge, da hab ich diesmal nicht aufgepasst 😉

    bis bald
    akari



  • Gut das werde ich dann mal ausprobieren!

    Vielen Dank! 🙂



  • @akari:

    So kannst du 100% sicher sein, das mit dem Löschen jeder Instanz der Form (also auch beim Beenden des Programms) auch die jeweilige Instanz von c_KLasse gelöscht wird.

    Nur damit ich jetzt richtig verstanden habe. Wenn ich z.B. das Programm durch
    "Application->Terminate();" beende, wird automatisch "cklasse" gelöscht und dessen Speicher wieder freigegeben?!
    Benötige ich dann noch einen Destruktor für c_Klasse, oder muss dieser nicht mehr definiert werden?
    Du hast ja gesagt, dass die Instanz "cklasse" durch das Beenden des Programms auch gelöscht wird (wenn ich das jetzt richtig verstanden habe 🙄 ), muss ich dann kein delete explizit angeben?

    So wie ich das jetzt verstanden habe, braucht man sich um die Speicheraufhebung jetzt keine Gedanken mehr zu machen, wenn man diesen auto_ptr benutzt. DAs Programm sorgt doch jetzt dafür, dass alle Instanzen und dessen Speicher wieder freigegeben werden (zusätzlich der Speicher, der durch mögliche komponentenreservierungen im konstruktor der jeweiligen Instanz), oder?

    Nochmals danke für die Antwort!

    Gruß



  • Instanzen von autp_ptr werden gelöscht, wenn sie den Gültigkeitsbereich verlassen. Also wennn sie lokal in einer Funktion sind, wenn die Funktion verlassen wird. Wenn sie Member einer Klasse sind, wenn der Destruktor dieser Klasse aufgerufen wird.
    Du mußt für deine eigene Klasse nur dann einen Destruktor erzeugen wenn du nichttriviele Member in deiner Klasse hast. Das wären z.Bsp. Pointer, denen du mit new Speicher zugewiesen hast. Wenn alle deine Variablen einfache Typen (PODs) oder Klassen mit eigenen Destruktor (AnsiString o.ä.) sind und keine weiteren Aufräumarbeiten stattfinden müssen, reicht auch der standardmäßig erzeugte Destruktor.
    Vor Verwendung von auto_ptr lies dir gründlich ein Tutorial dazu durch. auto_ptr hat mehrere Tücken (insbesondere Besitzübertragung). Der Name Autopointer impliziert mehr als er wirklich kann. Ich persönlich bevorzuge die boost Smartpointer.



  • Hallo

    Nur damit ich jetzt richtig verstanden habe. Wenn ich z.B. das Programm durch
    "Application->Terminate();" beende, wird automatisch "cklasse" gelöscht und dessen Speicher wieder freigegeben?!

    In meinem Beispiel ja. Und zwar weil dann die Instanz des Formes gelöscht wird, und damit über den auto_ptr auch die c_Klasse-Instanz.
    Achtung : das Form wird gelöscht, weil es so erstellt wird das es in dem Application-Objekt registriert ist. Man kann auch Form-Instanzen dynamisch erstellen, und diese nicht bei App. registrieren (new TForm1(0); ).
    In diesem Fall wird diese Instanz nicht automatisch mit Programmende gelöscht, und damit auch nicht der auto_ptr ausgelöst.

    Benötige ich dann noch einen Destruktor für c_Klasse, oder muss dieser nicht mehr definiert werden?

    Ob du für c_Klasse einen Destruktor brauchst oder nicht hat nichts mit auto_ptr oder der Lebenszeit zu tun. Sondern nur ob c_Klasse selber Speicher allokiert hat (in deinem Fall m_pSMTP, m_pStringList). Dieser muß wieder freigegebene werden, gleichgültig in welchem Zustand die Instanzen sind.

    bis bald
    akari



  • Kleine Frage am Rande dazu.
    Wenn ich z.B folgendes Objekt erzeuge:

    m_pPlaintext = new TIdText(m_pMessage->MessageParts,m_pStringListHTML);
       m_pPlaintext->ContentType="text/plain";
    

    kommt beim "delete m_pPlaintext" ein Fehler!

    m Projekt Projektname sind zu viele aufeinanderfolgende Exceptions aufgetreten: Anwendungsdefinierte Exception (Code xxxx) bei xxxx. Prozeß wurde angehalten. Fortsetzen mit Einzelne Anweisung oder Start.

    Woran kann das denn liegen? Weiß jemand eine Lösung


Anmelden zum Antworten