zeiger auf ungültiges objekt?
-
Mehrere Anmerkungen...
1. Bitte benutze die "C/C++"- statt der "Code"-Tags.
Scribbler schrieb:
class Foo { Bar *p_Bar; Foo() { p_Bar = new Bar; } ~Foo() { delete p_Bar; p_Bar = 0; } const Bar* get_Bar() const { return p_Bar; } }Mal ganz davon abgesehen das du dieses Beispiel so minimal gehalten hast das es nicht einmal laufen würde (public/private...), möchte ich weitere Mankos oder Anmerkungen dazu loswerden (zum Teil kann ich nur raten ob sie aufgrund des Minimalbeispieles nötig sind, oder auch in deinem realen Code zutreffen).
2. Ich würde dir raten dich mit der Initialisierungsliste eines Konstruktors vertraut zu machen, diese ist der nachträglichen Zuweisung im Konstruktor vorzuziehen.
3. In deinem Minimalbeispiel gibt es keinen sichtbaren Grund für Zeiger/new/delete, Objekte hätten hier ebenso gut ihre Arbeit getan. Dies liegt aber wohl eher an der Kürze des Codes.
4. In der Klasse sind zwingend der Kopierkonstruktor und Zuweisungsoperator erforderlich (Thema: Tiefe Kopie vs. flache Kopie).
5. Im Destruktor musst du den Zeiger nicht extra nach einen delete auf 0 setzen (Nach dem Destruktor existiert der Zeiger eh nicht mehr).
6. Mit Smartpointern würdest du dir hierbei das Leben vereinfachen können.Scribbler schrieb:
int main() { Foo *p_Foo = new Foo; Bar *p_Bar = new Bar; p_Bar = p_Foo -> get_Bar(); delete p_Foo; if(p_Bar != 0) { do_something(); } return 0; }Oje...
7. Siehe 3
8. Du legst hier "Bar *p_Bar = new Bar;" ein neues Objekt, mit dessen Adresse du p_Bar initialisierst, überschreibst aber gleich in der nächten Zeile "p_Bar = p_Foo->get_Bar();" die Adresse wieder. Das Vorher angelegte Objekt existiert aber noch und ist nun nicht mehr zugreifbar (Thema: Speicherleak).
9. Nach dem Delete des Objektes m_Foo wird der Zeiger p_Bar nicht auf 0 gesetzt, da Zeiger wie Integerwerte einen Wert halten (bei Zeigern eine Adresse), und keine Logik besitzen.cu André
-
Danke an Euch beide für die schnellen Antworten.
Der Code ist wirklich so klein gehalten um meine Problematik anzuzeigen, wurde leider nicht so deutlich dank des Fehlers der sich beim Minimalbeispiel eingeschlichen hat. Das Speicherleck gehört hier natürlich nicht hin, sorry für die Ausdrucksweise

class Foo { Bar *p_Bar; Foo() { p_Bar = new Bar; } ~Foo() { delete p_Bar; p_Bar = 0; //Knackpunkt 1 ist hier } const Bar* get_Bar() const { return p_Bar; } } int main() { Foo *p_Foo = new Foo; Bar *p_Baz = 0; //wir wollen hier kein leck erzeugen p_Baz = p_Foo -> get_Bar(); delete p_Foo; if(p_Baz != 0) //Knackpunkt 2 ist hier { do_something(); } return 0; }Was ich wissen wollte hast Du mir, André, glaube ich mit 9. beantwortet...
Habe ich Dich also richtig verstanden?ich kann Zeiger Foo::p_Bar auf 0 setzen,
der wird beim Löschen von p_Foo freigegeben,
wenn ich dann p_Baz in main() auf 0 prüfe,
wird der nicht zwangsläufig 0 enthalten,
obwohl der p_Baz zugewiesene Zeiger Foo::p_Bar vor dem Verlieren seiner Gültigkeit mit 0 belegt wurde?Japs, langer Satz
-
p_Baz = p_Foo -> get_Bar();An der Stelle erzeugst Du eine Kopie des Zeigers in p_Foo;
Merke: p_Baz hier ist ein anderer Zeiger als p_Bar aus der Klasse Foo. Die Zuweisung bewirkt lediglich das beide Zeiger auf den gleichen Speicherinhalt zeigen, trotzdem handelt es sich aber um zwei verschiedene Zeigervariablen.
delete p_Foo;Hier löschst Du p_Foo. p_Baz bliebt davon aber unberührt, zeigt also weiterghin auf das nunmehr ungültige Objekt im Speicher.
if(p_Baz != 0) //Knackpunkt 2 ist hierKlar, da p_Baz eine andere (Zeiger)Variable ist als p_Bar ist p_Baz auch nciht auf 0 gesetzt worden.
-
@loks:
Ja, das ist nachvollziehbar, danke.
Wie kann ich denn nachprüfen, ob das Objekt, auf den ein Pointer zeigt ungültig ist? Auf 0 prüfen ist ja somit keine sichere Lösung...
-
gar nicht. du bist derjenige, der das sicherstellen muss, denn du machst ja gerade die programmlogik, die dafür verantwortlich ist, das so etwas nicht passiert.
wenn du es dir einfach machen willst, dann verwende smartpointer wie z.b.
boost::shared_ptr, die (manche) können mitzählen, wieviele es von ihnen noch gibt und löschen das objekt, auf das sie zeigen erst, wenn kein andere smartpointer mehr darauf zeigt.
-
hmm...warum macht man sich denn dann noch die Mühe, überhaupt Zeiger auf 0 zu prüfen?
-
manchmal bietet eine funktion an, ihr einen zeiger als argument zu übergeben. das kann beispielsweise heißen, dass die funktion entweder ein gültiges objekt erwartet oder gar keines (NULL), aber dennoch zufrieden ist. du kannst also jedes beliebige objekt übergeben oder keines.
im gegensatz dazu gibt es referenzen, die nie NULL sein können. damit verlangst du also gerade, dass wer auch immer die funktion benutzt, ein gültiges objekt zur verfügung stellen muss. im regelfall benötigt man referenzen und keine zeiger.eine andere notwendigkeit (da weiß ich aber nicht, wie aktuell das noch ist), einen zeiger auf NULL zu setzen, ist im destruktor von exceptions, wenn sie speicher dynamisch allokiert haben.
-
Ja, stimmt. Oder ein 0-Zeiger als Abbruchbedingung...
Nochmals, vielen dank für die fixen Antworten.

-
Dein Code mal mit Smartpointern (gibt es verschiedene Varianten, dies ist nur ein Beispiel, und auch nicht vollständig). Ich treffe in diesem Beispiel 2 Voraussetzungen um den Code klein zu halten:
a) Kopie und Zuweisung dürfen auf das gleiche Objekt zugreifen (sprich: man teilt sich das Objekt)
b) Das Element darf auch die Oberklasse überleben
c) Der Code ist ungetestet#include <boost/scoped_ptr.hpp> #include <boost/shared_ptr.hpp> class Bar {}; class Foo { private: boost::shared_ptr<Bar> bar; public: Foo() : bar(new Bar) {} boost::shared_ptr<Bar> GetBar() const { return bar; } }; int main() { boost::shared_ptr<Bar> bar; { boost::scoped_ptr<Foo> foo(new Foo()); bar = foo->GetBar(); } // <-- foo wird gelöscht, bar aber da Referenzzählung nicht (shared_ptr) return 0; } // <-- bar wird gelöscht, da letzte Referenz weg istDies ist nur ein Beispiel, genaueres (und Ergänzungen wie weak_ptr etc. - was etwa das von dir gewünschte macht) musst du dir wenn bei interesse nachschauen. Die Boost-Bibliotheken sind kostenfrei im Internet erhältlich.
-
Danke asc, da werd ich mich mal einlesen...