Speicher freigeben, durch aufeinanderfolgende Zeiger



  • Hier noch ein Tool (Windows), welches dir hilft Memory-Leaks zu finden:
    https://vld.codeplex.com/

    Für Linux gibt es natürlich ähnliche Tools.



  • coder++ schrieb:

    Nunja, dann werd ich wohl anders testen müssen, ob das Objekt noch existiert.

    Man kann nicht testen ob ein Objekt noch existiert. Man muss es wissen.
    Also du kannst nen Zeiger auf das Objekt haben, und es dann zur Konvention machen dass du den Zeiger immer auf NULL setzt nachdem du das Objekt gelöscht hast.
    So lange dann keine weiteren Zeiger auf das Objekt existieren kannst du dadurch natürlich indirekt "testen" ob das Objekt noch existiert, indem du testest ob der Zeiger NULL ist oder nicht.

    Gibt es aber weitere Zeiger, die du beim Löschen des Objekts nicht NULL setzen kannst (weil du z.B. nicht weisst wo überall solche Zeiger sein könnten), dann hast du ein Problem.



  • ps:
    Google mal nach "programming ownership" (ohne die "").



  • theta schrieb:

    Hier noch ein Tool (Windows), welches dir hilft Memory-Leaks zu finden:
    https://vld.codeplex.com/

    Für Linux gibt es natürlich ähnliche Tools.

    Danke, werd ich mir mal anschauen.

    hustbaer schrieb:

    coder++ schrieb:

    Nunja, dann werd ich wohl anders testen müssen, ob das Objekt noch existiert.

    Man kann nicht testen ob ein Objekt noch existiert. Man muss es wissen.
    Also du kannst nen Zeiger auf das Objekt haben, und es dann zur Konvention machen dass du den Zeiger immer auf NULL setzt nachdem du das Objekt gelöscht hast.
    So lange dann keine weiteren Zeiger auf das Objekt existieren kannst du dadurch natürlich indirekt "testen" ob das Objekt noch existiert, indem du testest ob der Zeiger NULL ist oder nicht.

    Gibt es aber weitere Zeiger, die du beim Löschen des Objekts nicht NULL setzen kannst (weil du z.B. nicht weisst wo überall solche Zeiger sein könnten), dann hast du ein Problem.

    Danke.
    Das hab ich schon begriffen. Mit dem testen meinte ich eher, die Addressen aus allen Zeiger mit den gleichen Addressen entfernen.
    Achja, dachtest du dabei an soetwas: http://dl.acm.org/citation.cfm?id=582440
    ?


  • Mod

    coder++ schrieb:

    Das hab ich schon begriffen. Mit dem testen meinte ich eher, die Addressen aus allen Zeiger mit den gleichen Addressen entfernen.
    Achja, dachtest du dabei an soetwas: http://dl.acm.org/citation.cfm?id=582440
    ?

    Der Trick ist, dass so ein Fall nie vorkommen kann, wenn du bloß halbwegs modernes C++ nutzen würdest, anstatt von Hand mit Pointern rum zu frickeln.

    99% der typischen Anwendungsfälle manueller Speicherverwaltung kannst du mit den Containerklassen der Standardbibliothek erschlagen. Die verbliebenen 50% der Anwendungsfälle deckst du mit den Smartpointern der Standardbibliothek ab. Gerade deine aktuelle Beschreibung klingt so, als wolltest du shared_pointer, eventuell im Zusammenspiel mit weak_pointer. Das heißt nicht, dass das die beste Lösung ist, es ist nur was du oben beschreibst. Wie schon gesagt, ist zu 99% eine der Containerklassen besser geeignet. Bleiben noch 0.5% übrig für die ganz speziellen Fälle. Auch hier benutzt du keine rohen Zeiger, sondern du programmierst dir eine Klasse nach dem Vorbild der Container/Smartpointer, auf deine ganz speziellen 0.5% Anforderungen angepasst. Das heißt insbesondere, dass du RAII umsetzt. Das ist der wichtigste Punkt von allen und sorgt, wie oben schon gesagt, dass Fehler bei der Freigabe überhaupt gar nicht auftreten können*.

    *: Gleich kommen irgendwelche Klugscheißer mit hochgradig konstruierten Sprachvergewaltigung, um diese absolute Aussage zu widerlegen. Irgendwas mit reinterpret_casts, manuellen Destruktoraufrufen und Exceptions im Destruktor. Klar kann man sich absichtlich so dumm anstellen, dass man irgendeine Ausnahme findet. Aber unter normalen Umständen ist es nicht mehr möglich, etwas falsch zu machen, wenn man sich an die obigen Prinzipien hält.



  • SeppJ schrieb:

    coder++ schrieb:

    Das hab ich schon begriffen. Mit dem testen meinte ich eher, die Addressen aus allen Zeiger mit den gleichen Addressen entfernen.
    Achja, dachtest du dabei an soetwas: http://dl.acm.org/citation.cfm?id=582440
    ?

    Der Trick ist, dass so ein Fall nie vorkommen kann, wenn du bloß halbwegs modernes C++ nutzen würdest, anstatt von Hand mit Pointern rum zu frickeln.

    99% der typischen Anwendungsfälle manueller Speicherverwaltung kannst du mit den Containerklassen der Standardbibliothek erschlagen. Die verbliebenen 50% der Anwendungsfälle deckst du mit den Smartpointern der Standardbibliothek ab. Gerade deine aktuelle Beschreibung klingt so, als wolltest du shared_pointer, eventuell im Zusammenspiel mit weak_pointer. Das heißt nicht, dass das die beste Lösung ist, es ist nur was du oben beschreibst. Wie schon gesagt, ist zu 99% eine der Containerklassen besser geeignet. Bleiben noch 0.5% übrig für die ganz speziellen Fälle. Auch hier benutzt du keine rohen Zeiger, sondern du programmierst dir eine Klasse nach dem Vorbild der Container/Smartpointer, auf deine ganz speziellen 0.5% Anforderungen angepasst. Das heißt insbesondere, dass du RAII umsetzt. Das ist der wichtigste Punkt von allen und sorgt, wie oben schon gesagt, dass Fehler bei der Freigabe überhaupt gar nicht auftreten können*.

    *: Gleich kommen irgendwelche Klugscheißer mit hochgradig konstruierten Sprachvergewaltigung, um diese absolute Aussage zu widerlegen. Irgendwas mit reinterpret_casts, manuellen Destruktoraufrufen und Exceptions im Destruktor. Klar kann man sich absichtlich so dumm anstellen, dass man irgendeine Ausnahme findet. Aber unter normalen Umständen ist es nicht mehr möglich, etwas falsch zu machen, wenn man sich an die obigen Prinzipien hält.

    Ich benutze auch die Container-Klassen.
    Aber ich muss ein Element eines Objektes ständig "mitverfolgen".
    Da fiel mir als erstes ein Zeiger auf das Objekt ein.
    Ich schau mal, ob ich einen besseren Weg finde.
    Danke.



  • coder++ schrieb:

    Das hab ich schon begriffen.

    Gut. Begreifen viele nämlich nicht, bzw. erst sehr spät.

    coder++ schrieb:

    Mit dem testen meinte ich eher, die Addressen aus allen Zeiger mit den gleichen Addressen entfernen.

    Dazu kann man sich was basteln, wenn man es denn wirklich braucht.
    Wenns nicht threadsafe sein muss sollte ein shared_ptr<shared_ptr<Foo>> reichen.

    coder++ schrieb:

    Achja, dachtest du dabei an soetwas: http://dl.acm.org/citation.cfm?id=582440
    ?

    Nö. Eher an single ownership vs. shared ownership.
    http://stackoverflow.com/questions/13852710/single-vs-shared-ownership-meaning

    coder++ schrieb:

    Aber ich muss ein Element eines Objektes ständig "mitverfolgen".
    Da fiel mir als erstes ein Zeiger auf das Objekt ein.

    Wenn du etwas genauer beschreibst was ein Element eines Objektes ständig "mitverfolgen" bedeutet könnte dir vielleicht jmd. nen konkreteren Tip geben.



  • Nimm einfach shared_ptr. Ist zwar hier ziemlich verhasst aber das Ding macht in den meisten Fällen das Richtige.



  • hustbaer schrieb:

    Nö. Eher an single ownership vs. shared ownership.
    http://stackoverflow.com/questions/13852710/single-vs-shared-ownership-meaning

    Danke, seh ich mir an.

    hustbaer schrieb:

    Wenn du etwas genauer beschreibst was ein Element eines Objektes ständig "mitverfolgen" bedeutet könnte dir vielleicht jmd. nen konkreteren Tip geben.

    Im Grunde genommen, will ich das ein Objekt ein anderes verfolgt(also im dreidimemsionalen Raum). Beide Objekte sind auch im Code zwei verschiedene Objekte.
    Also dachte ich, ich könnte einfach die Addresse des zu verfolgenden Objektes in einen Zeiger laden, um so dann ständig die Position auszulesen.

    hustbaer schrieb:

    Dazu kann man sich was basteln, wenn man es denn wirklich braucht.
    Wenns nicht threadsafe sein muss sollte ein shared_ptr<shared_ptr<Foo>> reichen.

    Ethon schrieb:

    Nimm einfach shared_ptr. Ist zwar hier ziemlich verhasst aber das Ding macht in den meisten Fällen das Richtige.

    Okay, danke.



  • coder++ schrieb:

    Ich benutze auch die Container-Klassen.
    Aber ich muss ein Element eines Objektes ständig "mitverfolgen".
    Da fiel mir als erstes ein Zeiger auf das Objekt ein.
    Ich schau mal, ob ich einen besseren Weg finde.
    Danke.

    Das hört sich für mich nach Observer-Pattern an.



  • coder++ schrieb:

    hustbaer schrieb:

    Wenn du etwas genauer beschreibst was ein Element eines Objektes ständig "mitverfolgen" bedeutet könnte dir vielleicht jmd. nen konkreteren Tip geben.

    Im Grunde genommen, will ich das ein Objekt ein anderes verfolgt(also im dreidimemsionalen Raum). Beide Objekte sind auch im Code zwei verschiedene Objekte.
    Also dachte ich, ich könnte einfach die Addresse des zu verfolgenden Objektes in einen Zeiger laden, um so dann ständig die Position auszulesen.

    Observer-Pattern wurde ja schon genannt.
    Zwei Observer-Pattern Varianten:

    1. Die Position des Objekts ist "observable" - d.h. das Objekt benachrichtigt andere über Positionsänderungen.
    2. Die Lebenszeit des Objekts ist "observable" - d.h. das Objekt benachrichtigt andere wenn es zerstört wird.
      1 und 2 kann man natürlich auch kombinieren.

    Ansonsten bietet sich shared_ptr an.

    Wenn du willst dass der Verfolger das Ziel (den Verfolgten) am Leben hält, dann bekommt der Verfolger nen shared_ptr<Ziel> , und fertig.
    Das Ziel wird dann durch den Verfolger am Leben gehalten - es wird ja erst gelöscht wenn es keine shared_ptr mehr darauf gibt.

    Bzw. wenn du die Lebenszeit des Ziels unabhängig vom Verfolger kontrollieren willst, dann kannst du dem Verfolger auch einfach einen weak_ptr<Ziel> geben.

    Oder, wenn die Partei die die Lebenszeit des Ziels kontrolliert einen Level "überhalb" des Verfolgers angesiedelt ist, und das Verfolger Objekt auch "kennt", dann kannst du im Prinzip auch rohe Zeiger verwenden. Die Lebenszeit-kontrollierende Partei muss dann einfach nur verfolger->SetzeZiel(NULL) aufrufen bevor das Ziel gelöscht wird.
    Wobei das die am wenigsten "sichere" Variante ist. Weil Fehler die du dabei evtl. machst u.U. nicht oder erst spät auffallen.

    Wenn es sonst keine Punkte gibt die dagegensprechen (oder für eine andere Lösung) würde ich hier shared_ptr verwenden. Entweder, wenn das erwünscht ist, shared Ownership mit mehreren shared_ptr . Oder halt shared_ptr beim Owner und weak_ptr beim Verfolger.



  • Ich hab mir jetzt deinen Link zu single und shared ownership angesehen.

    Also wenn ich das richtig verstanden habe, wird bei einem unique_ptr der Speicher freigegeben, sobald er zerstört wird.
    Bei einem shared_ptr erst, wenn ALLE Referenzen zerstört werden.

    Also könnte ich doch mit mehreren Instanzen den gleichen Speicherbereich addressieren, sodass dieser erst freigegeben wird, sobald alle Instanzen zerstört werden, oder?

    Haltet ihr es generell sinvoll einen unique_ptr zu verwenden, anstatt eines standardmäßigen Zeigers? So muss man nicht daran denken, den Speicher selbst freizugeben und beugt so Speicherlücken(kann man so verlorenen Speicher bezeichnen?) vor.
    Einfacher ist es natürlich, Zeiger möglichst wenig zu verwenden und wenn dann vorsichtig mit ihnen umgehen, aber es steigert ja außerdem die Lesbarkeit und verhindert "Speichermüll".



  • coder++ schrieb:

    Haltet ihr es generell sinvoll einen unique_ptr zu verwenden, anstatt eines standardmäßigen Zeigers? So muss man nicht daran denken, den Speicher selbst freizugeben und beugt so Speicherlücken(kann man so verlorenen Speicher bezeichnen?) vor.

    In modernem C++ sind "besitzende" rohe Zeiger verpönt. Es ist also keine schlechte Idee unique_ptr zu verwenden.



  • MichelRT schrieb:

    coder++ schrieb:

    Haltet ihr es generell sinvoll einen unique_ptr zu verwenden, anstatt eines standardmäßigen Zeigers? So muss man nicht daran denken, den Speicher selbst freizugeben und beugt so Speicherlücken(kann man so verlorenen Speicher bezeichnen?) vor.

    In modernem C++ sind "besitzende" rohe Zeiger verpönt. Es ist also keine schlechte Idee unique_ptr zu verwenden.

    Okay, danke.
    Irgendwie hab ich das nie wirklich mitgekriegt, dass rohe Zeiger nicht mehr in modernes C++ gehören.



  • coder++ schrieb:

    Okay, danke.
    Irgendwie hab ich das nie wirklich mitgekriegt, dass rohe Zeiger nicht mehr in modernes C++ gehören.

    Doch schon, aber sie sollen eben nur zeigen und nichts anderes.
    Wenn man einen Besitz und damit die Pflicht zur Speicherfreigabe ausdruecken will, verwendet man soweit moeglich unique_ptr. Wenn es mehr als einen Besitzer gibt, sollte man erstmal sein Design ueberdenken und dann ggf. shared_ptr verwenden. shared_ptr schuetzt auch nicht in allen Situationen, z.B. bei zyklischen Abhaengigkeiten geht das nicht, die sollte man aber so gut wie nie haben.
    Nebenbei gilt noch, dass man fuer pointer auf Arrays ( int ptr = new int[10];* ) kein unique_ptr, sondern Container nehmen sollte.



  • Marthog schrieb:

    coder++ schrieb:

    Okay, danke.
    Irgendwie hab ich das nie wirklich mitgekriegt, dass rohe Zeiger nicht mehr in modernes C++ gehören.

    Doch schon, aber sie sollen eben nur zeigen und nichts anderes.
    Wenn man einen Besitz und damit die Pflicht zur Speicherfreigabe ausdruecken will, verwendet man soweit moeglich unique_ptr. Wenn es mehr als einen Besitzer gibt, sollte man erstmal sein Design ueberdenken und dann ggf. shared_ptr verwenden. shared_ptr schuetzt auch nicht in allen Situationen, z.B. bei zyklischen Abhaengigkeiten geht das nicht, die sollte man aber so gut wie nie haben.
    Nebenbei gilt noch, dass man fuer pointer auf Arrays ( int ptr = new int[10];* ) kein unique_ptr, sondern Container nehmen sollte.

    Achso. Ich dachte mit rohen Zeigern sind schon nur die Zeiger gemeint, welche Speicher angefordert haben.
    Und für Arrays benutze ich meistens sowieso vector und je nach dem auch map aus der Standardbibliothek.



  • coder++ schrieb:

    Irgendwie hab ich das nie wirklich mitgekriegt, dass rohe Zeiger nicht mehr in modernes C++ gehören.

    Das entscheidende ist der Zusatz "besizende" rohe Zeiger.
    Als Parameter für Funktionen/Methoden sind Zeiger natürlich nach wie vor erlaubt.
    Tabu ist natürlich das Speicher(also in Besitz nehmen) des so übergebenen Zeigers über die Lebenszeit der Funktion/Methode.



  • Ich wollte mal fragen, ob folgendes Minimal-Beispiel funktioniert, bzw. ob der Speicher erst nach löschen der Addressen aus beiden shared_ptren freigegeben wird.

    boost::shared_ptr<int> sp1(new int);
    
    boost::shared_ptr<int> sp2;
    sp2.reset(sp1.get()); // Es wird die Addresse, nicht der shared_ptr übergeben
    
    sp1.reset();
    std::cout << *sp2; /* "Weiß" sp1, dass sp2 existiert und hat den Speicher nicht freigegeben, oder eben nicht, wodurch der Speicher freigegeben und vielleicht schon neu belegt wurde? */
    sp2.reset();
    

    In diesem Beispiel geht es mir darum, ob ich den shared_ptr kopieren muss, oder ob es schon reicht die gleiche Addresse zu laden, da get() ja die wirkliche Addresse zurückgibt.



  • Das löscht zwei mal.



  • manni66 schrieb:

    Das löscht zwei mal.

    Der Speicher wird beides mal freigegeben?
    Was hab ich falsch gemacht?


Anmelden zum Antworten