Objekte löschen mit delete
-
Hi,
jetzt hab ich mir viele tolle Sachen ausgedacht, Konstruktoren und Destruktoren geschrieben und dann kommt immer eine EAccessViolation. Beim genauern Hinsehen ist mir aufgefallen, dass diese im Destruktor auftritt, wenn dieser zweimal hintereinander aufgerufen wird und versucht, Objekte freizugeben. Das kann ich nachvollziehen. Aber: ich verwende folgenden Code:
TMyClass *MyClass = new TMyClass(); ... if (MyClass) { delete MyClass; } ... if (MyClass) { delete MyClass; }
Ich war bisher immer davon ausgegangen, dass delete den Speicher freigibt und die Referenz auf NULL setzt. Offensichtlich ein Irrglaube. Muss ich den Zeiger manuell zurücksetzen, oder gibts da ne All-In-One-Lösung?
-
ja das alles in try/catch verpacken...
wie willst sonst fehler abfangen
-
try{
delete c++;
}catch(E_LOVE_CPP){
/* no catch */
}
-
Heimelchen schrieb:
Ich war bisher immer davon ausgegangen, dass delete den Speicher freigibt und die Referenz auf NULL setzt. Offensichtlich ein Irrglaube. Muss ich den Zeiger manuell zurücksetzen, oder gibts da ne All-In-One-Lösung?
Organisier deinen Code doch so, dass du gar nichts zurücksetzen musst, weil der Zeiger sofort nach dem delete aus dem Scope läuft. Und dann ersetz ihn durch einen Smartpointer, so dass du das delete weglassen kannst. Zack, 2 Fehlerquellen weniger.
try/catch ist natürlich Blödsinn.
-
DELETE_CPP: try{ delete c++; }catch(E_LOVE_CPP){ goto DELETE_CPP; }
-
delete ändert nichts am Pointer selber, sondern gibt nur den Speicher wieder frei. Was erlaubt ist, ist einen Pointer zu deleten, der auf 0 gesetzt ist. Daher ist die Lösung ganz einfach: Nach dem delete den Pointer auf 0 setzen, dann kannst du ihn so oft löschen wie du willst, d.h. das
if(MyClass)
brauchst du nicht mehr.
-
MFK schrieb:
Heimelchen schrieb:
Ich war bisher immer davon ausgegangen, dass delete den Speicher freigibt und die Referenz auf NULL setzt. Offensichtlich ein Irrglaube. Muss ich den Zeiger manuell zurücksetzen, oder gibts da ne All-In-One-Lösung?
Organisier deinen Code doch so, dass du gar nichts zurücksetzen musst, weil der Zeiger sofort nach dem delete aus dem Scope läuft. Und dann ersetz ihn durch einen Smartpointer, so dass du das delete weglassen kannst. Zack, 2 Fehlerquellen weniger.
try/catch ist natürlich Blödsinn.
ja das mir schon öfter aufgefallen das es für c++'ler blödsinn ist "new ..." in ein try/catch zu packen. da geht doch der fehler schon los und das zieht sich wie ein roter faden durch die gesamte software...
-
Ungültige Zeiger haben nicht auf magische Weise den Wert 0 (der im Prinzip auch nichts besonderes darstellt, außer dass er bei Konvertierung zu bool den Wert false bekommt). Das musst du schon selber machen.
Die All-in-One Lösung ist natürlich die Nichtbenutzung von Zeigern (braucht man ohnehin sehr selten) oder wenn doch dann Benutzung von intelligenteren Zeigerklassen (auto_ptr, shared_ptr). Normale Zeiger sieht man in C++ eher selten.
-
SeppJ schrieb:
Normale Zeiger sieht man in C++ eher selten.
so wie auch gute software
-
SeppJ schrieb:
Normale Zeiger sieht man in C++ eher selten.
Normale Zeiger sollte man in gutem C++ Code möglichst selten sehen.
Ebenso sollte man
new
möglichst selten sehen.Und fast gar nicht sollte man
delete
sehen (natürlich gibt es zu jedemnew
eindelete
, aber das sollte in irgendeinem dtor versteckt werden).
-
if (MyClass) { delete MyClass; }
uebrigens, die if Klausel ist vollkommen überfluessig.
TMyClass *MyClass = NULL; delete MyClass;
sollte ohne murren durchlaufen !!!
Ansonsten wie die Vorposter schon schreiben:
new und delete nur in verzweifelten Situationen ohne anderen Ausweg!Ciao ...
-
brotbernd schrieb:
SeppJ schrieb:
Normale Zeiger sieht man in C++ eher selten.
Normale Zeiger sollte man in gutem C++ Code möglichst selten sehen.
hast angst vor zeigern
-
Heimelchen schrieb:
Ich war bisher immer davon ausgegangen, dass delete den Speicher freigibt und die Referenz auf NULL setzt. Offensichtlich ein Irrglaube. Muss ich den Zeiger manuell zurücksetzen, oder gibts da ne All-In-One-Lösung?
Komische Frage. Du musst gar nichts. Du darfst einen ungültig gewordenen Zeiger aber nur zerstören, oder mit einem anderen gültigen Wert (inklusive Nullzeiger-Wert) füllen. Eine "All-In-One-Lösung" (Ich gehe mal davon aus, dass Du so etwas meinst:
template<class T> void mydelete(T*& ptr) {delete ptr; ptr=0;}
) ist gar nicht so praktisch, wie Du wahrscheinlich glaubst. Mir fehlt so eine Funktionalität wirklich nicht und ich würde sogar behaupten, dass keiner so etwas braucht. Das liegt wohl daran, dass ich weder
delete
nochdelete[]
benutze. Und die seltenen Gelegenheiten, in denen ichnew
verwende sehen dann meist so aus:auto_ptr<Dings> ap (new Dings(...));
...wobei man sich natürlch darüber im Klaren sein muss, was Kopierkonstruktor und Zuweisungsoperator von auto_ptr<> für böse Sachen machen...
kk
-
Hui, ist ja nen heißes Thema...
Da ich eigentlich aus der C-Welt komme, hab ich absolut nix gegen Zeiger. Ganz im Gegenteil, mit Zeigern und Casts ist man extrem mächtig; wenn man's kann. Allerdings hab ich eher Zeiger auf bereits belegten Speicher verwendet, darum brauchte ich fast nie was freigeben.
Intelligente Zeiger a'la auto_ptr sind zwar nett, erwartet aber eine Funktionen einen Onjektzeiger, kann ich ihm keinen auto_ptr geben. Will ich außerdem Objekte einer Klasse im Konstruktor erzeugen und im Destruktor freigeben, muss ich wohl new und delete verwenden.
try/catch ist hier Quatsch, weil es nicht die Antwort auf meine Frage ist. Ich will nicht die Fehlerausgabe verhindern, sondern die Ursache beseitigen. Verwende auch try/catch nur, wenn es nicht anders geht. Sonst programmier ich lieber richtig...
Wie ihr vielleicht festgestellt habt, war das nur ein Auszug aus dem Code. Ich wollte ursprünglich den Zeiger als Indikator verwenden: ist er NULL, gibt es keinen gültigen Inhalt (z.B. weil die benötigte Datenbankverbindung fehlt). Ist er nicht NULL, ist der Inhalt gültig. Jetzt hab ich der Klasse einen Indikator dafür spendiert und erzeuge sie im Form-Konstruktor und lösche sie im Form-Destruktor.
-
Heimelchen schrieb:
Will ich außerdem Objekte einer Klasse im Konstruktor erzeugen und im Destruktor freigeben, muss ich wohl new und delete verwenden.
class A { private: B b; // <- das wird im Konstruktor automatisch 'erzeugt' und im Destruktor automatisch 'gelöscht'. };
-
Heimelchen schrieb:
Intelligente Zeiger a'la auto_ptr sind zwar nett, erwartet aber eine Funktionen einen Onjektzeiger, kann ich ihm keinen auto_ptr geben.
Könntest du dies nochmal in klaren deutsch formulieren?
Heimelchen schrieb:
Will ich außerdem Objekte einer Klasse im Konstruktor erzeugen und im Destruktor freigeben, muss ich wohl new und delete verwenden.
Nicht zwangsweise, auch hier gehen normale Objekte ebenso. Und auch Smartpointer sind möglich.
class A { /.../ }; class B { private: A a_; std::tr1::shared_ptr<A> a2_; boost::scoped_ptr<A> a3_; public: B() : a_(), a2_(new A), a3_(new A) {} };
Heimelchen schrieb:
try/catch ist hier Quatsch...
Und ist ohnehin meist auch nicht sinnvoll für new behandelbar (Und nur wenn es behandelbar ist, macht ein unmittelbarer try/catch-Block Sinn - egal was hier registrierte Trolle von sich geben).
Heimelchen schrieb:
Jetzt hab ich der Klasse einen Indikator dafür spendiert und erzeuge sie im Form-Konstruktor und lösche sie im Form-Destruktor.
Könnte es sein, das du unter dem C++ Builder programmierst?
-
Heimelchen schrieb:
Da ich eigentlich aus der C-Welt komme, hab ich absolut nix gegen Zeiger. Ganz im Gegenteil, mit Zeigern und Casts ist man extrem mächtig; wenn man's kann.
Ich weiß nicht genau, worauf Du anspielst. Bedenke, dass eine Klasse relativ leicht seinen POD-Status verliert und damit viele Dinge aus der C-Trickkiste undefiniertes Verhalten hervorrufen würden.
Heimelchen schrieb:
Intelligente Zeiger a'la auto_ptr sind zwar nett, erwartet aber eine Funktionen einen Onjektzeiger, kann ich ihm keinen auto_ptr geben.
Kommt auf die Aufgabe der Funktion an. Ich finde auto_ptr schön selbst dokumentierend. Wenn ich ein
auto_ptr<abc> abstrakte_fabrik(...);
sehe, dann weiß ich sofort, wer für das Löschen des Objekts zuständig ist.
Heimelchen schrieb:
Will ich außerdem Objekte einer Klasse im Konstruktor erzeugen und im Destruktor freigeben, muss ich wohl new und delete verwenden.
Jein. Ausnahme-sichere Alternativen:
class MyClass { Foo f; // <- kein Zeiger Bar b; // <- kein Zeiger ... };
oder
class MyClass { boost::scoped_ptr<Foo> pf; boost::scoped_ptr<Bar> pb; ... };
Du solltest Dir schon einen anderen Programmierstil angewöhnen. Einen Stil, bei dem es keine Resourcen-Lecks geben kann, wenn irgendwo mal eine Ausnahme fliegt. Und einen Stil, bei dem nicht eine einzige Klasse gleich 5 Resourcen gleichzeitig "verwalten" muss in dem Sinne, dass ein delete oder close für alles im Destruktor benötigt wird.
Heimelchen schrieb:
try/catch ist hier Quatsch, weil es nicht die Antwort auf meine Frage ist. Ich will nicht die Fehlerausgabe verhindern, sondern die Ursache beseitigen.
Re-Design.
Heimelchen schrieb:
Wie ihr vielleicht festgestellt habt, war das nur ein Auszug aus dem Code. Ich wollte ursprünglich den Zeiger als Indikator verwenden: ist er NULL, gibt es keinen gültigen Inhalt (z.B. weil die benötigte Datenbankverbindung fehlt). Ist er nicht NULL, ist der Inhalt gültig. Jetzt hab ich der Klasse einen Indikator dafür spendiert und erzeuge sie im Form-Konstruktor und lösche sie im Form-Destruktor.
Wenn Du es für nötig hältst, den meisten Deiner Klassen einen Destruktor zu verpassen, dann machst Du wahrscheinlich etwas falsch.
Gruß,
kk
-
Ja, ich programmiere unterm C++-Builder.
Dass ich aus der C-Welt komme und gern mit Zeigern arbeite, heißt nur genau das, was es heißt. Da kann man schnell über ein paar Strukturzeiger riesige Datenmengen in wenigen Zeilen Code verwalten. Ich verzichte deswegen aber nicht auf "normale" Instanzen von Objekten und Variablen oder sonst was.
Wenn mein Programmierstil so schlecht ist, hätte ich gern zwei Beispiele zum bessermachen:
1. Eine Klasse soll eine ADOConnection enthalten.
2. Diese Klasse soll ein weiteres Objekt enthalten, das diese ADOConnection als Konstruktor-Parameter bekommt.Aktuelle Lösung:
class TMyClass { private: TADOConnection *ADOConnection; TMyChild *MyChild; public: TMyClass(); ~TMyClass(); } TMyClass::TMyClass() { this->ADOConnection = new TADOConnection(NULL); this->MyChild = new TMyChild(this->ADOConnection); } TMyClass::~TMyClass() { delete this->ADOConnection; delete this->MyChild; }
-
Wie siehts damit aus?
class TMyClass { private: TADOConnection ADOConnection; TMyChild MyChild; public: TMyClass(); } TMyClass::TMyClass() : ADOConnection(0), MyChild(&ADOConnection) { }
-
12 vs 21