Forum: PC-Programmierung externe Daten-Pointer aufräumen nach Objekt gelöscht wurde


von olli23 (Gast)


Lesenswert?

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?

von Peter II (Gast)


Lesenswert?

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?

von Baku M. (baku)


Lesenswert?

Das klingt nach einer grundsätzlich verquasten Architektur.
Wie kommen deine Zeiger denn überhaupt nach 'extern' (wo immer das sein 
mag)?

von Theor (Gast)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von olli23 (Gast)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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).

von Theo (Gast)


Lesenswert?

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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

Wilhelm M. schrieb:
> In modernem C++ ...

Bezieht sich das evtl. auf meinen Beitrag resp. meine Beiträge?

von der mechatroniker (Gast)


Lesenswert?

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.

von Theor (Gast)


Lesenswert?

Theor schrieb:
> Wilhelm M. schrieb:
>> In modernem C++ ...
>
> Bezieht sich das evtl. auf meinen Beitrag resp. meine Beiträge?

Es scheint jedenfalls so und würde inhaltlich passen.

Ich wollte mich auch eigentlich nur bedanken, Wilhelm.

von Wilhelm M. (wimalopaan)


Lesenswert?

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 ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Theor schrieb:
> Theor schrieb:
>> Wilhelm M. schrieb:
>>> In modernem C++ ...
>>
>> Bezieht sich das evtl. auf meinen Beitrag resp. meine Beiträge?

Ja, wer hier mitdiskutiert, läuft Gefahr ...

>
> Es scheint jedenfalls so und würde inhaltlich passen.
>
> Ich wollte mich auch eigentlich nur bedanken, Wilhelm.

de nada

von Theor (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Theor schrieb:
>> Theor schrieb:
>>> Wilhelm M. schrieb:
>>>> In modernem C++ ...
>>>
>>> Bezieht sich das evtl. auf meinen Beitrag resp. meine Beiträge?
>
> Ja, wer hier mitdiskutiert, läuft Gefahr ...

...,  dass er angesprochen wird, ohne das sein Name genannt wird. :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.