Ist "delete[]" zwingend erforderlich?
-
Hallo,
mir stellt sich gerade die Frage, ob das "delete[]", welches dafür sorgt, dass die Pointer-Variable wieder freigegeben wird, zwingend erforderlich ist. Die Pointer-Variable wird in meinem Code nur dazu verwendet, um etwas in einen String-Array abzuspeichern. Sie wird danach nicht wieder benutzt. Ich empfinde es also nicht wirklich wichtig, die Pointer-Variable wieder "freizugeben". Ist das richtig oder stoße ich damit auf eine gewisse Unsauberkeit?
-
Kannst sie ruhig hängen lassen. Aber klar, wenn Du so Code postetst, gibts automatisch Mecker.
-
Speicher, der mit
newreserviert wurde, der aber nicht mitdelete/delete[]wieder freigegeben wird, verursacht ein Speicherleck. So einfach ist das.
-
Aus diesem Grund verwendet man auch kein new[] und delete[].
Für ein String-Array bietet sich std::vectorstd::string an, die machen new[] und delete[] automatisch.
-
Ich gehe davon aus, daß es eine globale Variable ist, oder eine in der main() nicht innerhalb einer Schleife, also daß sie tatsächlich nur einmal im Programm genewt wird und nur einmal am Ende des Programms ein delete[] aufgerufen werden sollte.
Dann kann man mal ausnahmsweise π rational sein lassen.
-
dachschaden schrieb:
Speicher, der mit
newreserviert wurde, der aber nicht mitdelete/delete[]wieder freigegeben wird, verursacht ein Speicherleck. So einfach ist das.Und ist das schlimm? Immer?
-
Na ja, ich habe jetzt überall das delete[] eingefügt. Unsicher bin ich mir noch hier:
string function1() { string *Pointer; Pointer = new string[Index]; //Index wird aus einer anderen Funktion "geholt" delete[] Pointer; return Pointer; }Hier gibt es eine Exception. Und zwar, weil das Array ja nicht mehr existiert. Deswegen habe ich das so gemacht:
string function1() { string *Pointer; Pointer = new string[Index]; //Index wird aus einer anderen Funktion "geholt" string result = Pointer[1]; delete[] Pointer; return result; }Das ist doch die einzigste Möglichkeit. Oder geht das noch einfaher? Ich möchte explizit bei Arrays bleiben, also nicht zu vector<string> wechseln.
-
Das ist genau das Problem, in das du dich selber bringst.
Schau mal, das wirkt so als hättest du selber Angst vor dem Speicher, den du da auf dem Heap allokierst.
Dein Code wirkt auf mich wie: Schnell, gib mir hier was aufm Heap, aber dann lösch es schnell wieder weil ich Angst habe, dass ich es sonst später vergesse!
Aber die Daten will ich trotzdem haben, bitte!
-
Unabhängig vom delete machen machen die beiden Versionen verschiedene Dinge. Die Erste dürfte eigentlich nicht übersetzt werden.
-
volkard schrieb:
Ich gehe davon aus, daß es eine globale Variable ist, oder eine in der main() nicht innerhalb einer Schleife, also daß sie tatsächlich nur einmal im Programm genewt wird und nur einmal am Ende des Programms ein delete[] aufgerufen werden sollte.
Dann kann man mal ausnahmsweise π rational sein lassen.Ja, aber wieso der manuelle Aufwand?
Und wenn mans einfach leaken lässt: bei std::string wärs egal, der Speicher wird eh zurückgegeben. Aber wenns eine komplexere Resource, dessen Destruktor nicht aufgerufen wird, ist das blöd.
-
http://en.wikipedia.org/wiki/Dangling_pointer
http://de.wikipedia.org/wiki/Hängender_ZeigerWas ich anfordere gebe ich auch ordentlich zurück.
EDIT:
Man lässt pointer nicht einfach so hängen. Auch pointer haben Gefühle.
-
FPR schrieb:
http://en.wikipedia.org/wiki/Dangling_pointer
http://de.wikipedia.org/wiki/Hängender_Zeiger
Was ich anfordere gebe ich auch ordentlich zurück.Naja, das ist so in alten Sprachen wie Pascal oder C/C++.
Man stelle sich Folgendes vor:
Oma bestellt einen Aufzug durch Druck auf den Knopf.
Sie wartet ein Weilchen bis er da ist und steigt ein und fährt hin.
Oma gibt nach Ausssteigen ihn zurück durch erneuten Druck auf den Knopf.Jo, alle von uns würden das als gestört empfinden. So genau sehen alle modernen Programmierer jedes delete als gestört.
~(Sorum argumentiert es sich leichter.)~
-
Siehe mein edit, volkard.
Ausserdem hat Oma nach dem Aussteigen keinen Knopf wie "Erdgeschoß" von Aussen.
Vergleich hinkt.
-
string function1() { string *Pointer; Pointer = new string[Index]; //Index wird aus einer anderen Funktion "geholt" string result = Pointer[1]; delete[] Pointer; Pointer = 0; return result; }Wäre das dann so richtig?
-
Navy schrieb:
string function1() { string *Pointer; Pointer = new string[Index]; //Index wird aus einer anderen Funktion "geholt" string result = Pointer[1]; delete[] Pointer; Pointer = 0; return result; }Wäre das dann so richtig?
Zeile 4 kann eine Exception werfen.
Was soll das ganze Konstrukt überhaupt? Für eine theoretische Demonstration, wie das mit dem new und delete (wenn man es denn mal benutzt) richtig ginge, hat das denkbar viele Fehler (nämlich so ziemlich alle, wegen derer man new und delete nicht so ungekapselt benutzt). Und für einen echten, praktischen Code hat das total unnötige, ungekapselte new und delete.
-
Wie immer bin ich weit weniger höflich als SeppJ.
Code ist Quatsch. Ganz offensichtlich hast du keine Ahnung was du tust.
-
FPR schrieb:
http://en.wikipedia.org/wiki/Dangling_pointer
http://de.wikipedia.org/wiki/Hängender_ZeigerWas ich anfordere gebe ich auch ordentlich zurück.
EDIT:
Man lässt pointer nicht einfach so hängen. Auch pointer haben Gefühle.Das klingt jetzt für mich so, als wenn Du da zwei Dinge verwechselst. Hängende Zeiger entstehen ja gerade deswegen, weil das, worauf sie zeigen, frei gegeben wird und man den (oder einen anderen, der dieselbe Adresse speichert) zu lange am Leben lässt.
-
krümelkacker schrieb:
FPR schrieb:
http://en.wikipedia.org/wiki/Dangling_pointer
http://de.wikipedia.org/wiki/Hängender_ZeigerWas ich anfordere gebe ich auch ordentlich zurück.
EDIT:
Man lässt pointer nicht einfach so hängen. Auch pointer haben Gefühle.Das klingt jetzt für mich so, als wenn Du da zwei Dinge verwechselst. Hängende Zeiger entstehen ja gerade deswegen, weil das, worauf sie zeigen, frei gegeben wird und man den (oder einen anderen, der dieselbe Adresse speichert) zu lange am Leben lässt.
Da hast du recht. Mea culpa.
-
Okay, dann versuche ich das jetzt einmal nachzuvollziehen.
Der neu angeforderte Speicher muss wieder freigegeben werden, damit der RAM nicht irgendwann zu voll und zu langsam wird. Wenn dann der Pointer freigegeben wurde, hat dieser anscheinend aber noch die alten Arrays gespeichert/hat noch einen Inhalt. Wenn jetzt einer neuen Variable zufällig der vorherig verwendete, eigentlich freigegebene Pointer zugewiesen werden würde, hätte diese neue Variable dann den Inhalt des eigentlich freigegebenen alten Pointers. Wird bei der Variable jetzt abgefragt, ob diese bspw. leer ist, wird zurückgegeben, dass die Variable nicht leer ist, weil sie eben einem nicht gelöschten Pointer zugewiesen wurde. Also muss der Pointer "gelöscht" werden. Also wäre es doch richtig, nach dem "delete[] Pointer" "Pointer = NULL" zu schreiben. Oder ist das nicht so?SeppJ schrieb:
Zeile 4 kann eine Exception werfen.
Was soll das ganze Konstrukt überhaupt? Für eine theoretische Demonstration, wie das mit dem new und delete (wenn man es denn mal benutzt) richtig ginge, hat das denkbar viele Fehler (nämlich so ziemlich alle, wegen derer man new und delete nicht so ungekapselt benutzt). Und für einen echten, praktischen Code hat das total unnötige, ungekapselte new und delete.Zeile 4 wirft eine Exception aus, wenn vorher nicht Pointer geleert wurde. So verstehe ich das

Aber wie soll ich denn sonst während der Laufzeit Speicher anfordern, wenn ich new und delete nicht benutzen darf? Was meint gekapselt eigentlich genau? Soll ich new und delete in eine eigene Funktion "auslagern"?
-
make_unique für unique_ptr (war glaube ich C++14), make_shared für shared_ptr (C++11).
Oder
std::unique_ptr<T> pointer(new T());
-
Navy schrieb:
Okay, dann versuche ich das jetzt einmal nachzuvollziehen.
Der neu angeforderte Speicher muss wieder freigegeben werden, damit der RAM nicht irgendwann zu voll und zu langsam wird. Wenn dann der Pointer freigegeben wurde, hat dieser anscheinend aber noch die alten Arrays gespeichert/hat noch einen Inhalt. Wenn jetzt einer neuen Variable zufällig der vorherig verwendete, eigentlich freigegebene Pointer zugewiesen werden würde, hätte diese neue Variable dann den Inhalt des eigentlich freigegebenen alten Pointers. Wird bei der Variable jetzt abgefragt, ob diese bspw. leer ist, wird zurückgegeben, dass die Variable nicht leer ist, weil sie eben einem nicht gelöschten Pointer zugewiesen wurde. Also muss der Pointer "gelöscht" werden. Also wäre es doch richtig, nach dem "delete[] Pointer" "Pointer = NULL" zu schreiben. Oder ist das nicht so?Nein, du hast da ein paar wirklich falsche Vorstellungen. Beispielsweise gibt es keine "leeren" Variablen, man kann daher sowas auch nicht abfragen. Ich denke, dein Hauptproblem ist, dass du das Freigeben mit dem Zeiger verbindest. Es ist aber gar nicht der Zeiger, der freigegeben werden muss, sondern das Objekt bzw. das Array, auf das der Zeiger zeigt, falls dieses mit new bzw. new[] erzeugt wurde.
Wenn du ein Objekt freigibst, und den Zeiger, der nach wie vor auf dieselbe Adresse zeigt, wieder verwendest, können folgende Dinge passieren:
- Es wurde inzwischen ein anderes Objekt an dieser Stelle erzeugt. Du liest bzw. veränderst also unkontrolliert Daten. Spezialfall: Es wurde ein Objekt desselben Typs an derselben Stelle erzeugt, dann kann dein Programm sogar mal scheinbar funktionieren.
- An der Stelle liegt kein Objekt, aber dafür gewisse Daten, die die Speicherverwaltung intern benötigt. Wenn du da rumpfuschst, bekommst du irgendwann später einen Absturz.
- Der freigegebene Speicher wurde an das Betriebssystem zurückgegeben, ein Zugriff auf eine nun ungültige Adresse führt direkt zum Absturz.