Zeiger = 0;
-
RussianTux schrieb:
Shade Of Mine schrieb:
Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.
Ein Speicherleck entsteht?
Exakt.
Alles was du mit new anlegst musst du ja mit delete löschen. Und wenn du vergisst wo das Ding ist, dass du angelegt hast - kannst du ja nicht mehr mit delete löschen. Ergo: ja, Speicherleck.
-
Shade Of Mine schrieb:
RussianTux schrieb:
Shade Of Mine schrieb:
Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.
Ein Speicherleck entsteht?
Exakt.
Alles was du mit new anlegst musst du ja mit delete löschen. Und wenn du vergisst wo das Ding ist, dass du angelegt hast - kannst du ja nicht mehr mit delete löschen. Ergo: ja, Speicherleck.class Data; struct Pointer { Data* data; }; int main(void) { Pointer* P1 = new Pointer; P1->data = new Data(//blablaa); delete P1->data; delete P1; return 0; }Wäre dies der richtige Weg um 'data' und 'P1' loszuwerden oder könnte man "delete P1->data;" überspringen und direkt bei "delete P1;" anfangen?
-
Ist ein möglicher Weg so wie du es dastehen hast.
Wenn du P1 zuerst löschst und dann nochmal darauf zugreifst, sei es nur um data zu löschen, dann erzeugst du undefiniertes Verhalten.
Es kann gut gehen. Das Programm kann aber auch abstürtzen. Es können in China auch in dem Moment dann 20 Reissäcke umfallen. Der Standard garantiert dir für nichts.Alternativ könntest du natürlich einen Destruktor in deiner Struktur definieren, der dann data platt macht, denn dann reicht der aufruf von delte P1;
-
RussianTux schrieb:
Wäre dies der richtige Weg um 'data' und 'P1' loszuwerden oder könnte man "delete P1->data;" überspringen und direkt bei "delete P1;" anfangen?
Nein. Der als "richtig" geltende Weg wäre, dass ein Objekt sich selber um seine Ressourcen kümmert (oder besser noch sie an Ressourcenhandlerklassen auslagert) und sie auch insbesondere selber wieder freigibt:
Google: RAIIint main(void)
{
Pointer* P1 = new Pointer;//...
delete P1;
}Dies ist totaler Mist. Wenn ein new und ein dazugehöriges delete im gleichen Scope stehen, dann ist dies das gleiche wie ein Objekt mit automatischer Speicherklasse, bloß in schlecht (sowohl Performance als auch Probleme beim unerwartetem Verlassen des Scopes). Kommst du von Java?
-
Vielleicht will er auch nur den Umgang mit Zeigern üben

-
hustbaer schrieb:
Vielleicht will er auch nur den Umgang mit Zeigern üben

Und das geht nur, indem man fehlerhaften Mist produziert?
-
hustbaer schrieb:
Vielleicht will er auch nur den Umgang mit Zeigern üben

So ist es, habe das Thema Zeiger nochmals üben müssen weil ich da noch viele Lücken in der Theorie freigelassen habe.
class Data; class Pointer { private: Data* data; public: Pointer(Data new_data) { data = new Data(new_data); } ~Pointer() { if(data != 0) delete data; } }; int main(void) { Data d; Pointer* P1 = new Pointer(d); // "delete P1;" weggelassen da nicht erforderlich return 0; // objekt wird beim beenden automatisch gelöscht }Möglicher Lösungsweg oder wieder Fehlversuch?
-
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.RussianTux schrieb:
// "delete P1;" weggelassen da nicht erforderlich return 0; // objekt wird beim beenden automatisch gelöscht }Wird es nicht.
Richtig wäre es so:
class Pointer { private: std::unique_ptr<Data> data; public: Pointer(Data new_data) : data(new Data(new_data)) { } }; int main(void) { Data d; std::unique_ptr<Pointer> P1(new Pointer(d)); // "delete P1;" weggelassen da nicht erforderlich return 0; // objekt wird beim beenden automatisch gelöscht }
-
TyRoXx schrieb:
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.RussianTux schrieb:
// "delete P1;" weggelassen da nicht erforderlich return 0; // objekt wird beim beenden automatisch gelöscht }Wird es nicht.
SeppJ schrieb:
int main(void)
{
Pointer* P1 = new Pointer;//...
delete P1;
}Dies ist totaler Mist. Wenn ein new und ein dazugehöriges delete im gleichen Scope stehen, dann ist dies das gleiche wie ein Objekt mit automatischer Speicherklasse, bloß in schlecht (sowohl Performance als auch Probleme beim unerwartetem Verlassen des Scopes). Kommst du von Java?
[quote="TyRoXx"]
RussianTux schrieb:
Richtig wäre es so:
class Pointer { private: std::unique_ptr<Data> data; public: Pointer(Data new_data) : data(new Data(new_data)) { } }; int main(void) { Data d; std::unique_ptr<Pointer> P1(new Pointer(d)); // "delete P1;" weggelassen da nicht erforderlich return 0; // objekt wird beim beenden automatisch gelöscht }std::unique_ptr<T> kommt hier nicht infrage, das ist eine Übungsaufgabe dessen Aufwand nicht gleich die Standard-lib beansprucht, es geht darum zu verstehen was genau hier abläuft
-
RussianTux schrieb:
Möglicher Lösungsweg oder wieder Fehlversuch?
So ziemlich alles falsch.
Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.
Zeile 16: Beschwör lieber ein paar Voodoogötter, das bringt mehr.
Klasse Pointer: Regel der großen Drei verletzt.
Zeile 26: Falscher Unsinn.
Zeile 29: Falscher, gefährlicher Unsinn.Aus deiner Antwort auf TyRoXx entnehme ich, dass du nicht weißt, was automatische Speicherklasse ist.
-
RussianTux schrieb:
std::unique_ptr<T> kommt hier nicht infrage, das ist eine Übungsaufgabe dessen Aufwand nicht gleich die Standard-lib beansprucht, es geht darum zu verstehen was genau hier abläuft
In C++ läuft das aber genau so ab.
deleteist nicht dazu da, direkt verwendet zu werden.
Was ist eigentlich so schwer daran zu verstehen, dassunique_ptrbei seiner Zerstörungdeleteaufruft?
-
TyRoXx schrieb:
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.
-
SeppJ schrieb:
RussianTux schrieb:
Möglicher Lösungsweg oder wieder Fehlversuch?
So ziemlich alles falsch.
Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.
Irgendwas in der Initialisierungsliste zu machen ist hier weder notwendig noch bringt es irgendwas.
Rohe Zeiger bleiben dumm, auch wenn man sie in der Initialisierungsliste initialisiert.
-
camper schrieb:
TyRoXx schrieb:
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.Ich wette das ist nicht das was RussianTux sich gedacht hat. RussianTux denkt nämlich bestimmt, dass new den Wert 0 zurück geben würde, wenn ein Fehler auftritt. Was natürlich nicht der Fall ist.
-
hustbaer schrieb:
SeppJ schrieb:
RussianTux schrieb:
Möglicher Lösungsweg oder wieder Fehlversuch?
So ziemlich alles falsch.
Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.
Irgendwas in der Initialisierungsliste zu machen ist hier weder notwendig noch bringt es irgendwas.
Rohe Zeiger bleiben dumm, auch wenn man sie in der Initialisierungsliste initialisiert.Wenn new hier eine Exception schmeißt, wird der Destruktor aufgerufen und es kracht, weil der Zeiger irgendeinen undefinierten Wert hat. Wenn eine Initialisierungsliste benutzt würde, dann würde korrekt abgeräumt.
-
SeppJ schrieb:
camper schrieb:
TyRoXx schrieb:
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.Ich wette das ist nicht das was RussianTux sich gedacht hat. RussianTux denkt nämlich bestimmt, dass new den Wert 0 zurück geben würde, wenn ein Fehler auftritt. Was natürlich nicht der Fall ist.
Vermutlich. Nur fällt das eben unter die Kategorie "Zeiger kann gar nicht 0 sein" und damit ist TyrOXx Begründung schlicht falsch.
Ich bin dafür, diese Abfrage zu eleminieren, wo sie sinnlos ist. Ich habe aber etwas gegen die Begründung dieses Vorhabens damit, dass delete auch Nullzeiger nimmt.
-
SeppJ schrieb:
hustbaer schrieb:
SeppJ schrieb:
RussianTux schrieb:
Möglicher Lösungsweg oder wieder Fehlversuch?
So ziemlich alles falsch.
Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.
Irgendwas in der Initialisierungsliste zu machen ist hier weder notwendig noch bringt es irgendwas.
Rohe Zeiger bleiben dumm, auch wenn man sie in der Initialisierungsliste initialisiert.Wenn new hier eine Exception schmeißt, wird der Destruktor aufgerufen und es kracht, weil der Zeiger irgendeinen undefinierten Wert hat. Wenn eine Initialisierungsliste benutzt würde, dann würde korrekt abgeräumt.
Nö.
Es werden nur vollständig konstruierte Objekte zerstört
Und als vollständig konstruiert gilt das Objekt erst, wenn der Konstruktor regulär verlassen wurde (=ohne eine Exception zu werfen).Deswegen muss man sich ja in C++ auch für jeden Fliegenschiss eigene RAII Helper-Klassen basteln.
-
camper schrieb:
Ich bin dafür, diese Abfrage zu eleminieren, wo sie sinnlos ist. Ich habe aber etwas gegen die Begründung dieses Vorhabens damit, dass delete auch Nullzeiger nimmt.
In den allermeisten Fällen finde ich die Begründung dass delete auch Nullzeiger nimmt vollkommen ausreichend.
Wenn der "Normalfall" der ist, dass gelöscht werden muss, spielt die Performance keine Rolle (wer optimiert schon auf den Fehlerfall?), und man spart sich eine Zeile = übersichtlicher = juchui.
Bzw. auch wenn es ganz regulär vorkommen kann, aber der minimale Performance-Vorteil der "mit if" Variante einfach nicht ins Gewicht fällt: eine Zeile gespart -> juchui.
In Fällen wo es häufig vorkommen kann dass der Zeiger wirklich Null ist, und in denen der Performanceunterschied möglicherweise nicht irrelevant ist kann bzw. sollte man das "if" stehen lassen.
Und nochwas: DER #1 Grund warum Leute das "if" schreiben ist weil sie nicht wissen dass delete auch Nullzeiger nimmt. Daher finde ich den Hinweis darauf immer gerechtfertigt.
-
hustbaer schrieb:
camper schrieb:
Ich bin dafür, diese Abfrage zu eleminieren, wo sie sinnlos ist. Ich habe aber etwas gegen die Begründung dieses Vorhabens damit, dass delete auch Nullzeiger nimmt.
In den allermeisten Fällen finde ich die Begründung dass delete auch Nullzeiger nimmt vollkommen ausreichend.
Wenn der "Normalfall" der ist, dass gelöscht werden muss, spielt die Performance keine Rolle (wer optimiert schon auf den Fehlerfall?), und man spart sich eine Zeile = übersichtlicher = juchui.
Bzw. auch wenn es ganz regulär vorkommen kann, aber der minimale Performance-Vorteil der "mit if" Variante einfach nicht ins Gewicht fällt: eine Zeile gespart -> juchui.
Dann ist das der Grund: Übersichtlichkeit und kürzerer Code. Das sollte dann auch so geschrieben werden. Das liegt nämlich - jedenfalls für micht - nicht so eindeutig auf der Hand. So gibt man die schöne Symmetrie zwischen erfolgreicher Allokation und Deallokation auf. Das macht es nicht unbedingt einfacher, die Korrektheit zu überprüfen.
hustbaer schrieb:
In Fällen wo es häufig vorkommen kann dass der Zeiger wirklich Null ist, und in denen der Performanceunterschied möglicherweise nicht irrelevant ist kann bzw. sollte man das "if" stehen lassen.
Persönlich ist mir das Performance-Argument ziemlich egal, ich habe es nur gebracht, weil sich manche Leute davon überzeugen lassen. In den Fällen wo es relevant werden kann (Smartpointer), weiß man das in der Regel schon.
hustbaer schrieb:
Und nochwas: DER #1 Grund warum Leute das "if" schreiben ist weil sie nicht wissen dass delete auch Nullzeiger nimmt. Daher finde ich den Hinweis darauf immer gerechtfertigt.
Es besteht allerdings immer noch ein Unterschied zwischen DIngen, die man bloß tun kann und solchen, die man tun sollte.
TyRoXx schrieb:
RussianTux schrieb:
if(data != 0) delete data;deletenimmt gerne Nullzeiger entgegen.Ist für mich eine Aufforderung, das if wegzulassen. Die bloße Möglichkeit, das tun zu können, ist eben kein Grund, es zu tun. Sein->Sollen = Fehlschluss.
-
@camper
Was du schreibst stimmt alles. Was ich aber nicht rauslesen konnte, ist wann du jetzt persönlich empfehlen würdest das "if" zu schreiben und wann nicht. Bzw. wann du es selbst schreibst und wann nicht.Ich schreibe es nur in Fällen wo es einen relevanten Performance-Unterschied geben könnte.
Und zwar hauptsächlich deswegen, weil es eben redundant ist.
Das "if" kommuniziert für mich "der Pointer könnte NULL sein". Und das impliziert für mich wiederum "überall wo kein if steht ist er garantiert nicht NULL". Und das wiederum ist in Projekten an denen mehrere Leute arbeiten einfach nicht wahr. Kurz: es ist irreführend.