Hallo,
ich beschäftige mich ein wenig mit C++ und bin auf die Funktion new
gestoßen. Ich frage mich wozu das sinnvoll sein soll.
Wenn ich z.B. einen Pointer deklariere:
1
int*pointer;
Dann zeigt er schon auf eine Speicheradresse, ohne das ich ihm eine
übergebe.
Habe das geprüft mit:
1
cout<<&pointer;
Das lässt doch darauf schließen das er schon Speicher reserviert oder
nicht? Das selbe passiert doch hier auch:
1
int*pointer=newint;
Hat jemand vielleicht ein Besipiel wo das new tatsächlich einen Nutzen
hat?
Grüße
foo* x ist ein unintialisierter Pointer. Wenn du mit dem irgendwas
machst außer rumkopieren (zum Beispiel *x = a oder was auch immer) ist
das undefiniertes Verhalten und lässt im Zweifel dein Programm
abstürzen.
Pointer schrieb:> Das lässt doch darauf schließen das er schon Speicher reserviert oder> nicht?
Nein. Das lässt nicht darauf schließen.
Ein Pointer zeigt immer irgendwohin. Das liegt in der Natur der Sache;
ein Pointer ist nur eine Adresse.
Aber das, worauf er zeigt, ist, solange er nicht initialisiert wird,
halt irgendwas. Was da halt zufällig im Speicher 'rumdümpelt (sofern
an der betreffenden Adresse überhaupt Speicher ist).
Pointer schrieb:> Ja ok, bisschen blöd ausgedrückt.> Das heißt doch aber, dass für diesen Pointer Speicher reserviert wurde> oder nicht?
ja, aber nur für den Pointer und nicht auf das wo er hinzeigt.
Für den Pointer selber ja, also 4 oder 8 Bytes. Aber nicht für das
Objekt, auf das der Pointer zeigt. Der Pointer selber liegt ja einfach
auf dem Stack.
Rufus Τ. F. schrieb:> Aber das, worauf er zeigt, ist, solange er nicht initialisiert wird,> halt irgendwas. Was da halt zufällig im Speicher 'rumdümpelt (sofern> an der betreffenden Adresse überhaupt Speicher ist).
Achso, also sage ich mit dem new, dass dieser Adressbereich tatsächlich
für den zukünftigen Inhalt von dem Pointer reserviert wird?
Aber moment? Dann wäre es doch eine Variable.
Irgendwie verstehe ich gerade garnichts mehr.
Man kann's eigentlich nicht verstehen wenn man in zu abstrakten
Begriffen denkt. Geh mal einen Schritt zurück.
Erstmal gibt es den Heap und den Stack. Speicher auf dem Stack wird
immer implizit reserviert, indem man einfach sowas hinschreibt wie
Foo x;
Dann kriegst du sizeof(Foo) Bytes an Stack-Speicher für ein Objekt des
Typs Foo. &x ist die Adresse von dem Stack-Speicher. Wenn du schreibst
Foo* x;
dann kriegst du sizeof(Foo*) (das ist eigentlich immer 4 oder 8) Bytes
an Stack-Speicher für ein Objekt des Typs Foo* (das ist eigentlich immer
eine Zahl mit 4 oder 8 Bytes). Das ist aber nur der Pointer, den
eigentlichen Inhalt des Objekts kannst du bisher noch nirgends ablegen.
Speicher auf dem Heap wird explizit reserviert, indem man das
Betriebssystem danach fragt. Das tun Funktionen wie malloc oder new.
Wenn du schreibst
new Foo;
dann kriegst du eine Adresse zurück, an der das Betriebssystem
sizeof(Foo) Bytes an Speicher für dich reserviert hat. Außerdem wird der
Konstruktur von Foo aufgerufen, wobei this auf den neu reservierten
Speicher zeigt.
Wenn du
Foo* x = new Foo
schreibst, passiert beides. Es werden sizeof(Foo*) Bytes auf dem Stack
reserviert, und sizeof(Foo) Bytes auf dem Heap, und der erste
Speicherbereich wird mit der Adresse des zweiten befüllt.
In modernem C++ versucht man explizite Verwendung von new meist zu
vermeiden, weil aller Speicher der mit new reserviert wird auch mit
delete wieder freigegeben werden muss (Speicher auf dem Stack hingegen
wird automatisch implizit wieder freigegeben, sobald der Kontrollfluss
die nächste schließende } erreicht). Vergisst man dass, hat man ein sog.
Memory Leak. Dazu gibt es so Konzepte wie std::shared_ptr.
OK!
Ich glaube ich habe es verstanden.
Also:
Alle Variablen die in Funktionen deklariert werden landen auf dem Stack.
Das bedeutet auch, das nach dem durchlaufen der Funktion der Speicher
wieder freigegeben wird und die Variable ist nicht mehr existent.
Globale Variablen hingegen landen auf dem Heap. Wenn ich jetzt z.B. eine
Variable erzeugen möchte, ohne das ich eine Globale Variable Deklariere
kann ich das also mit dem "new" innerhalb einer Funktion machen. So kann
ich dann auf dem Heap Speicher reservieren und dann einfach mit einem
Zeiger darauf zugreifen. Dieser Speicher bleibt dann auch nach dem
Verlassen der Funktion weiterhin reserviert.
Und stimmt, dafür ist das delete gut. Weil wenn ich ja den Zeiger lösche
bleibt der Platz weiterhin reserviert und dadurch entsteht dann wohl
dieser Memory Leak.
Ja?
Ein Pointer ist eine Variable, in der eine Adresse gespeichert ist.
Mit
int *pointer;
bekommst du einen Zettel, auf dem du schreiben kannst, wo dein Objekt
liegt.
Einen Platz für dein Objekt kannst du dir z.B. mit
pointer = new int;
besorgen.
Das &pointer sagt dir nur, wo dein Zettel liegt (nicht was drauf steht).
Danke auch für die vielen Antworten. Ich habe mich ein wenig unglücklich
ausgedrückt. Mir ging es auch viel mehr darum "WARUM" man das mit dem
new überhaupt macht, wenn ich nicht gleich einfach eine Globale Variable
machen kann. Hätte doch den selben Effekt. Aber ich glaube ich habe es
mir oben selbst erklärt.
Pointer schrieb:> Aber ich glaube ich habe es> mir oben selbst erklärt.
Aber ich glaube ich habe es mir mit euren guten Antworten erklären
können. <- So klingt das besser :-)
new nimmt man eben für alles dynamische wo man vorher noch nicht weiss
wie gross es wird. ZB eine Datei in den Speicher holen: erst die Grösse
lesen, dann mit new ein Array passender Grösse reservieren.
Pointer schrieb:> Globale Variablen hingegen landen auf dem Heap.
Der Heap ist nur für dynamisch erzeugte Daten da. Statische Daten, also
solche, die Compiler/Linker bereits bekannt sind, sind weder Teil des
Stacks noch Teil des Heaps. Egal ob die im gesamten Programm bekannt
sind (global), nur im File (static ausserhalb Funktion) oder nur in der
Funktion (static innerhalb Funktion).
In
char *p = malloc(1000);
oder
X *p = new X;
geht es um zwei völlig getrennte Daten. Die per malloc/new auf dem
Heap erzeugten Daten, und den wenig Bytes grossen Pointer, der auf sie
zeigt.
Der Vollständigkeit halber: Lokale Daten (ohne static) darf man sich
zwar konzeptionell so vorstellen, dass sie stets auf dem Stack landen.
Aber man sollte sich nicht wundern, wenn sie in der Realität dort nicht
immer auftauchen. Weil oft in Registern verschwindend, sobald optimiert
übersetzt wird.
Pointer schrieb:> Alle Variablen die in Funktionen deklariert werden landen auf dem Stack.
Grundsätzlich ja.
Genauer gesagt: Die lokal definierten Variablen werden (normalerweise)
auf dem Stack platziert (es gibt CPU/Compiler-typische Ausnahmen) oder
aber auch im Data/Code Segment (Stichwort "static").
> Das bedeutet auch, das nach dem durchlaufen der Funktion der Speicher> wieder freigegeben wird und die Variable ist nicht mehr existent.
Grundsätzlich ja.
Ausnahme sind "static" Variablen (Data/Code Segment).
Der Begriff "Freigeben" wird bei lokalen Variablen eher nicht benutzt.
Die Variablen verlassen den "Scope". (einfach mal im Web suchen, bspw.
http://c.learncodethehardway.org/book/ex22.html).
> Globale Variablen hingegen landen auf dem Heap.
Besser ist die Bezeichnung "dynamische" Variablen.
"Globale" Variablen hingegen liegen im Data oder Code Segment (genauso
wie local static).
> Wenn ich jetzt z.B. eine> Variable erzeugen möchte, ohne das ich eine Globale Variable Deklariere> kann ich das also mit dem "new" innerhalb einer Funktion machen. So kann> ich dann auf dem Heap Speicher reservieren und dann einfach mit einem> Zeiger darauf zugreifen. Dieser Speicher bleibt dann auch nach dem> Verlassen der Funktion weiterhin reserviert.
Ja. Dynamisch allokierte Speicherbereiche sind gültig bis sie
freigegeben werden (malloc/free). Mit new/delete ist es ähnlich.
> Und stimmt, dafür ist das delete gut. Weil wenn ich ja den Zeiger lösche> bleibt der Platz weiterhin reserviert und dadurch entsteht dann wohl> dieser Memory Leak.>> Ja?
Genau.
Dynamische Speicherbereiche werden idR benutzt, wenn die Größe zur
Compilezeit nicht bekannt (bspw. der Inhalt einer zu ladenden Datei)
oder wenn sehr viel Speicher notwendig ist (da die Stack Größe häufig
begrenzt ist).
Pointer schrieb:> Danke auch für die vielen Antworten. Ich habe mich ein wenig unglücklich> ausgedrückt. Mir ging es auch viel mehr darum "WARUM" man das mit dem> new überhaupt macht, wenn ich nicht gleich einfach eine Globale Variable> machen kann. Hätte doch den selben Effekt.
Es gibt drei Arten von - nennen wir es Objektlebensdauern, nämlich
statisch, automatisch und dynamisch.
Alle globalen Variablen sowie die als static deklarierten Member- und
lokalen Variablen haben statische Lebensdauer. Das heißt, sie existieren
bis zum Programm-Ende (nach Rückkehr aus main()) und werden dann
aufgeräumt.
Alle nicht-statischen lokalen Variablen haben automatische Lebensdauer.
Sie existieren bis zum Ende des Blocks, in dem sie definiert sind.
Alles, was von new oder malloc und Konsorten kommt, ist dynamisch und
existiert so lange, bis man es explizit delete oder free wieder
freigibt.
Die dynamischen Objekte haben den Vorteil, daß ich sie nur dann erzeugen
muss, wenn ich sie auch tatsächlich brauche. Außerdem kann ich dann auch
z.B. bei einem Array entscheiden, wie groß es sein soll, wenn ich weiß,
wie viel Platz ich benötige. Bei statischen und automatischen Arrays
dagegen muß die Größe zur Compilezeit definiert sein. Auch bei der
objektorientierten Programmierung im Zusammenhang mit Polymorphie wird
das wichtig, da ich dann auch zur Laufzeit wählen kann, welchen
dynamischen Typ ein neues Objekt haben soll.