externe Daten-Pointer aufräumen nach Objekt gelöscht wurde

Gast #4893600
Hallo,

ich habe eine Klasse welche einige Daten auf dem Heap alloziert und die 
Zeiger in Arrays ablegt. Beim aufrufen des Destruktors wird der Speicher 
mit delete wieder freigegeben. Soweit so klar.

Aus Performance Gründen habe ich aber noch weitere Orte, an denen ich 
Zeiger auf die Objekte ablegen muss.

Wenn ich jetzt das Objekt lösche bleiben diese Zeiger ja erhalten und 
können wenn sie nicht genullt werden viel kaputt machen.

Was gibt es denn für Methoden so etwas elegant zu lösen?
Hatte überlegt in dem Objekt noch eine Liste zu führen mit Zeigern auf 
die Zeiger die beim Löschen zu nullen sind, aber das gibt dann ja einen 
ewigen Rattenschwanz.

Jemand eine bessere Idee?
Gast
olli23 schrieb:
> Wenn ich jetzt das Objekt lösche bleiben diese Zeiger ja erhalten und
> können wenn sie nicht genullt werden viel kaputt machen.

das versteht ich nicht.


einmal muss der Speicherbereich gelöscht werden der mit new/malloc 
angefordert wurde, wenn weiter Zeiger vorhanden sind müssen die nicht 
noch mal gelöscht werden. Wenn sie in einer Liste sind, dann muss nur 
die Liste gelöscht werden. Aber was meinst du mit dem nullen?
Gast
Die sozusagen "primitivste" oder auch naheliegendste Methode wäre meiner 
Meinung nach, die Verwaltung der Zeiger selbst in ein Objekt zu kapseln; 
und für jeden Zeiger einen Referenzierungszähler einzuführen. Der 
Grundgedanke ist dabei, das es nur einen Ort gibt, an dem die Zeiger 
gespeichert werden. Oder anders herum: Zeiger zu duplizieren (aus 
Performance-Gründen wie Du sagst) ist an sich eine schlechte Idee 
(relativ und zu einem gewissen Grad auch eine Meinungssache).

Das jedenfalls Zeiger in mehreren Objekten verwendet werden widerspricht 
meiner Meinung nach dem Kapselungsprinzip. Entweder die obige "primitive 
Lösung" oder sonst eine (Vererbung, Interface-Methoden) die in das 
Objekt-Paradigma passt.
olli23 schrieb:
> Aus Performance Gründen habe ich aber noch weitere Orte, an denen ich
> Zeiger auf die Objekte ablegen muss.

Warum?

Und warum sollte der "andere" Code auf die Idee kommen wollen diese 
Kopien der Zeiger noch zu verwenden nachdem sich das ganze Programm doch 
schon darüber einig geworden ist daß die besagten Objekte auf dem Heap 
entsorgt (und somit offensichtlich gar nicht mehr gebraucht) werden? 
Bekommt dieser andere Teil das nicht mitgeteilt, oder kann er das nicht 
vorher erfragen?
olli23 schrieb:
> Hallo,
>
> ich habe eine Klasse welche einige Daten auf dem Heap alloziert und die
> Zeiger in Arrays ablegt. Beim aufrufen des Destruktors wird der Speicher
> mit delete wieder freigegeben. Soweit so klar.
>
> Aus Performance Gründen habe ich aber noch weitere Orte, an denen ich
> Zeiger auf die Objekte ablegen muss.
>
> Wenn ich jetzt das Objekt lösche bleiben diese Zeiger ja erhalten und
> können wenn sie nicht genullt werden viel kaputt machen.
>
> Was gibt es denn für Methoden so etwas elegant zu lösen?


std::shared_ptr<> ist das Mittel der Wahl.
Gast
Wilhelm M. schrieb:
> std::shared_ptr<> ist das Mittel der Wahl.

nicht zwingend. Man muss sich nur selber klar sein, wem die Daten 
gehören.

shared_ptr und recoucencoutner sind nicht immer sinnvoll wenn es um 
perfomance geht. (was hier ja scheinbar wichtig ist).
Peter II schrieb:
> Wilhelm M. schrieb:
>> std::shared_ptr<> ist das Mittel der Wahl.
>
> nicht zwingend. Man muss sich nur selber klar sein, wem die Daten
> gehören.

Und genau das scheint dem TO eben nicht klar zu sein.

Im übrigen denke ich auch, dass er std::shared_ptr und std::weak_ptr 
braucht.

>
> shared_ptr und recoucencoutner sind nicht immer sinnvoll wenn es um
> perfomance geht. (was hier ja scheinbar wichtig ist).

Nicht notwendigerweise: einen leicht erhöhten Aufwand hat man nur beim 
Erzeugen der Objekte.
Gast
Vielen Dank für die vielen schnellen Antworten.

nochmal etwas expliziter beschreiben das Problem:

ich habe eine Baumartige Datenstruktur (mit mehren 100 Objekten). Beim 
anlegen der Baumstruktur wird der Speicher alloziert.
Es gibt dort einige Objekte auf die ich sehr oft zugreifen muss in einer 
festgelegten Reihenfolge.

Damit ich jetzt nicht jedes mal den kompletten Baum durchsuchen muss 
(was einen Großteil der Performance zerstören würde) sind Zeiger zu 
allen diesen Objekten die man oft braucht zusätzlich zu der Baumstruktur 
in einer Liste hinterlegt. Dann muss man nur einmal den Baum durchsuchen 
und in die Liste eintragen und danach nur noch die Liste abarbeiten.

@wimalopaan: der shared_ptr sieht auf den ersten Blick gut aus. Das 
schaue ich mir mal an.
Gast
Wilhelm M. schrieb:
> [...]
> std::shared_ptr<> ist das Mittel der Wahl.

Vermutlich ist das so, jedenfalls wenn das Problem tatsächlich aus 
Performance-Gründen anders nicht zu lösen ist. Der Ratschlag scheint mir 
jedenfalls sinnvoll.

Mir schien aber das Problem mit der Frage zu sein, dass so wenig vom 
Kontext bekannt ist.

Ein Anfänger bzw. jemand der auf so ein Problem das erste Mal stösst 
(aber ansonsten fortgeschritten ist) sollte meiner Meinung nach, eine 
eigene Lösung des Problems versuchen. Deswegen meine, im wesentlichen, 
Wiedergabe der Implementierung von shared_ptr ohne es ausdrücklich zu 
nennen. Er sollte aber auch noch einmal durchdenken, ob dieser Weg 
wirklich notwendig ist. Deswegen meine (relativierte) Kritik und der Rat 
es mit bisher bekannten Mitteln innerhalb der bekannten Teile des 
Paradigmas zu versuchen.

Falls dem TO das Vorhandensein von std::shared_ptr lediglich zeitweise 
entfallen war, spricht sicher nichts dagegen. Falls aber nicht, sollte 
er sich wenigstens mal die Implementierung anschauen, würde ich sagen.
Deine Liste ist an sich nur ein Cache. Wenn du an den Originaldaten (dem 
Baum) etwas änderst, musst du nur den Cache invalidieren. In deinem Fall 
also einfach die Liste leeren und neu aufbauen (währenddessen musst du 
natürlich alle Nutzer der Liste kurz anhalten).
Gast
olli23 schrieb:
> [...]
> nochmal etwas expliziter beschreiben das Problem:
>
> ich habe eine Baumartige Datenstruktur (mit mehren 100 Objekten). Beim
> anlegen der Baumstruktur wird der Speicher alloziert.
> Es gibt dort einige Objekte auf die ich sehr oft zugreifen muss in einer
> festgelegten Reihenfolge.
>
> Damit ich jetzt nicht jedes mal den kompletten Baum durchsuchen muss
> (was einen Großteil der Performance zerstören würde) sind Zeiger zu
> allen diesen Objekten die man oft braucht zusätzlich zu der Baumstruktur
> in einer Liste hinterlegt. Dann muss man nur einmal den Baum durchsuchen
> und in die Liste eintragen und danach nur noch die Liste abarbeiten.

Dann scheinen mir meine Vorbehalte nicht unnötig gewesen zu sein. Ich 
hoffe nur, ich habe das alles richtig verstanden.


Wenn Du ohnehin eine "Liste" hast, die prominente Blätter des Baums 
beinhalten, dann ist da ja schon die nötige Verwaltungsstruktur.

Auf die greifst Du zu und irgendwann im Ablauf, hörst Du damit endgültig 
auf. Dann kannst Du Liste löschen - musst aber nicht die Zeiger auf die 
Blatt-Objekte löschen. Die müssten, formal, ja ihre eigenen Destruktoren 
haben, die aufgerufen werden, wenn der Baum wieder gelöscht wird. Du 
musst nur durch den Programmablauf sicherstellen, dass eines nach dem 
anderen geschieht. (Erst Liste löschen dann Baum).
In modernem C++ verzichtet man auf ein explizites new/delete. Deswegen 
std::unique_ptr bzw. std::shared_ptr zusammen mit den Helpern 
std::make_shared und std::make_unique. Diese beiden Zeigertype zeigen 
die Art der Eigentümershaft ganz klar an und man nichts mehr falsch 
machen. Ggf. braucht man std::weak_ptr, um einen Zeiger zu haben, der 
keine Eigentümerschaft ausdrückt, jedoch promoted werden kann zu einem 
SmartPointer.

Rohe Zeiger werden nur noch als Zeiger ohne Eigentümerschaft verwendet 
bspw. als Iteratoren. Manchmal werden sogar die rohen Zeiger auch noch 
in ein Template verpackt, um ggf. ein delete auszuschließen.

Natürlich steht es einem frei, auch so etwas wie einen managed-pointer 
wie etwa QPointer einzuführen. Braucht man aber wegen std::weak_ptr 
nicht wirklich.
Gast
Das Problem ist hier doch aber eins, das nichts primär mit Pointern zu 
tun hat. Es gibt hier mehrere Indizes in dieselben Daten, ohne einen 
Mechanismus, diese mehreren Indizes konsistent zu halten. Das 
Pointerproblem ist nur ein Auswuchs davon.

Die Lösung wäre imho, diese spezielle Datenstruktur in eine Klasse 
MyFancyMultiIndexedInMemoryDatabase o.ä. zu kapseln und dafür zu sorgen, 
dass sie nur über deren Methoden manipuliert werden kann, die dann dafür 
zuständig sind, alle Indizes zugleich so zu ändern, dass sie konsistent 
bleiben.
der mechatroniker schrieb:
> Das Problem ist hier doch aber eins, das nichts primär mit Pointern zu
> tun hat. Es gibt hier mehrere Indizes in dieselben Daten, ohne einen
> Mechanismus, diese mehreren Indizes konsistent zu halten. Das
> Pointerproblem ist nur ein Auswuchs davon.
>
> Die Lösung wäre imho, diese spezielle Datenstruktur in eine Klasse
> MyFancyMultiIndexedInMemoryDatabase o.ä. zu kapseln und dafür zu sorgen,
> dass sie nur über deren Methoden manipuliert werden kann, die dann dafür
> zuständig sind, alle Indizes zugleich so zu ändern, dass sie konsistent
> bleiben.

Letztlich kann man SmartPointer als genau das auffassen ...

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 9 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.