Wie Speicherleck vermeiden?



  • Hallo @nn, danke für Deine interessanten Lösungsansätze.

    1. Wenn ich das richtig verstehe kann ich mit

    delete pListe
    

    dann scheinbar von außen auf das Innere eines abgeschlossenen Blocks zugreifen, wo die TStringList erzeugt wurde. Ich dachte der Block sei geschützt und soetwas ginge nicht? 😕

    2. Zu shared_ptr<TStringList> TForm1::GetStringList(void)
    Ich habe in der Hilfe des Builders unter boost gesucht, leider erfolglos. Kannst Du mir noch ein bißchen weiterhelfen, wo ich etwas mehr dazu finden kann?



  • Die boost libraries sind beim C++Builder nicht dabei.
    Herunterladen, alles in deinen include-Ordner entpacken und fertig.

    http://www.boost.org/

    thunderbol4 schrieb:

    1. Wenn ich das richtig verstehe kann ich mit

    delete pListe
    

    dann scheinbar von außen auf das Innere eines abgeschlossenen Blocks zugreifen, wo die TStringList erzeugt wurde. Ich dachte der Block sei geschützt und soetwas ginge nicht? 😕

    Du hast da einen Zeiger, der auf irgendeine Speicherstelle zeigt, an der ein TStringList-Objekt konstruiert wurde. Den Zeiger kannst du so oft kopieren wie du willst (und das machst du hier, indem ihn zurückgibst) und kannst die Zeiger so oft zerstören wie du willst (passiert hier beim Verlassen der Funktion, wenn der Scope, in dem Picklist existiert, zu Ende geht) und das alles juckt die Speicherstelle gar nicht.
    Erst wenn du delete auf irgendeinen dieser Zeigerkopien anwendest, ist die Speicherstelle nicht mehr verfügbar (und das dort hinterlegte TStringList-Objekt gelöscht).



  • Oder du bastelst dir ganz einfach eine Klasse drumherum, das sich um das anlegen und löschen der TStringList kümmert.

    [cpp]
    class Tmy
    {
    private:
    TStringList liste;
    public:
    TStringList
    GetStringList(void)
    {
    liste->Clear();
    liste->Add("Hallo, hört");
    liste->Add("mich jemand");
    return liste;
    }
    Tmy(){liste = new TStringList();}
    ~Tmy(){delete liste;}
    };



  • Die boost libraries sind beim C++Builder nicht dabei.

    Und laufen auch erst ab Builder 2009 ...



  • booster schrieb:

    Die boost libraries sind beim C++Builder nicht dabei.

    Und laufen auch erst ab Builder 2009 ...

    Da sind sie dann aber dabei. Er hat ja nicht geschrieben welche Version er hat.

    thunderbol4 schrieb:

    Hallo @nn, danke für Deine interessanten Lösungsansätze.
    Ich dachte der Block sei geschützt und soetwas ginge nicht? 😕

    Die Variable Picklist ist nach dem Funktionsaufruf nicht mehr da. Aber das worauf sie zeigt schon. Ich glaube du hast den Sinn von Zeigern noch nicht verstanden.



  • Erst 'mal recht vielen Dank für Eure Antworten. 👍 👍

    @Nanyuki, danke für den Link zu boost, habe das nur 'mal ganz kurz angeklickt. Ich werde mich da aber noch intensiv reinlesen.

    Zu boost libraries:

    @nn schrieb:

    Und laufen auch erst ab Builder 2009 ... Da sind sie dann aber dabei. Er hat ja nicht geschrieben welche Version er hat.

    Benutze den Builder2009. Wußte bislang nichts von boost. Werde mich dann 'mal in meinem Builder-Verzeichnis auf die Suche machen.

    @nn schrieb:

    Ich glaube du hast den Sinn von Zeigern noch nicht verstanden.

    Ich stimme Dir uneingeschränkt zu. Bis gestern war ich der Ansicht ich müßte eine mit new erzeugte Variable im gleichen Block wieder mit delete entfernen.
    Jetzt denke ich habe ich Euch verstanden. Man kann eine mit new erzeugte Variable auch an irgendeiner anderen passenden Stelle des Programms wieder mit delete löschen, also auch außerhalb des Blocks, indem sie erzeugt wurde. Wichtig scheint nur zu sein, dass man dazu einen Zeiger genau dieses Typs auf diese erzeugte Variable hat. Die Variable müßte, wenn ich das richtig verstehe, in einem Art "globalen" Speicherbereich stehen.
    Bitte korrigiert mich, falls das doch nicht richtig ist. 😃



  • Tailer schrieb:

    Oder du bastelst dir ganz einfach eine Klasse drumherum, das sich um das anlegen und löschen der TStringList kümmert.

    class Tmy
    {
       private:
       TStringList *liste;
       public:
       TStringList* GetStringList(void)
       {
          liste->Clear();
          liste->Add("Hallo, hört");
          liste->Add("mich jemand");
          return liste;
       }
       Tmy(){liste = new TStringList();}
       ~Tmy(){delete liste;}
    };
    

    Du musst den Kopierkonstruktor und Zuweisungsoperator noch privat machen oder vernünftig implementieren, sonst fliegen dir Sachen wie

    int main()
    {
       TMy a1, a2;
       TMy a3 = a2;
       a2 = a1;
    }
    

    um die Ohren.



  • Würde mich mal interessieren warum das so ist und wie die Abhilfe dabei aussieht.



  • Wenn nicht explizit ein Kopierkonstruktor oder der Zuweisungsoperator definiert werden erzeugt ihn der Compiler. Die Defaultoperationen für beide erzeugt eine bitweise Kopie von PODs bzw. führt den Kopierkonstruktor/Zuweisungsoperator für non-POD member aus. In diesem Fall (pointer to TStringList) wird eine bitweise Kopie des Pointers erzeugt, der auf den gleichen Speicherbereich wie das Original zeigt. Beim Zerstören wird der Speicherbereich dann zwei Mal freigegeben und das führt zu undefiniertem Verhalten.
    Bei der einfachsten Lösung macht man den Kopierkonstruktor und den Zuweisungsoperator privat, damit er nicht aufgerufen werden kann.
    Will man Kopiersemantiken unterstützen muss man sich Gedanken machen, welches Verhalten man will. Man könnte z.B. eine tiefe Kopie erzeugen, indem man in den beiden Funktionsrümpfen neue StringListen erzeugt und den Inhalt der Original StringList hineinkopiert (ungetestet):

    class TMy
    {
       TStringList* StringList_;
    public:
       TMy()
       {
          StringList_ = new TStringList();
       }
    
       TMy( const TMy& op )
       {
          // Kopie der StringList erzeugen
          clone( op.StringList_ );
       }
    
       TMy& operator=( const TMy& op )
       {
          // Selbstzuweisung verhindern
          if( this != &op )
          {
             // Kopie der StringList erzeugen
             clone( op.StringList_ );
          }
          return *this;
       }
    
       // Zugriffsmethoden
       TStringList* get_stringlist()
       {
          return StringList_;
       }
    
       const TStringList* get_stringlist() const
       {
          return StringList_;
       }
    
    private:
       void clone( TStringList* op )
       {
          if( NULL != op )
          {
             TStringList* tmp = new TStringList();
             for( int i = 0; i < op->Count; ++i )
             {
                // aus´m Kopf, kA, ob das so korrekt ist. Jedenfalls sollen die
                // Elemente von op nach tmp kopiert werden
                tmp->Add( op->Strings->String[i );
             }
             // alte StringList zerstören und neue zuweisen
             delete StringList_;
             StringList_ = tmp;
          }
       }
    };
    

    Eine andere Möglichkit ist z.B. Referenzzählung, wo mehrere TMy Objekte tatsächlich auf eine StringListe zeigen und intern ein Referenzzähler geführt wird, wieviele Referenzen auf die StringList zeigen. Beim Zerstören eines TMy Objektes wird dann die Referenz um 1 vermindert und beim Zerstören der letzten Referenz wird die StringList auch zerstört (so arbeitet boost::shared_ptr).



  • booster schrieb:

    Und laufen auch erst ab Builder 2009 ...

    Ich verwende boost seit dem BCB5. Es sind nur einige wenige Libs die nicht (oder nicht vollständig) funktionieren. shared_ptr geht aber schon immer.


Anmelden zum Antworten