Forum: PC-Programmierung c++ wann Objekt auf Heap


von c_newbie (Gast)


Lesenswert?

Hallo,

zwei Fragen:

ich habe ein c++ Objekt z.B.
1
class Objekt
2
{
3
public:
4
    Objekt(const Matrix4 &data);
5
    ~Objekt();
6
    methoden();
7
private:
8
    Matrix4 local_0;
9
    Matrix4 local_1;
10
    Matrix4 local_2;
11
    Matrix4 local_3;
12
};

wenn ich jetzt ein Objekt über
1
objekt1 = new Obejkt(param);
erstelle, befinden sich dann alle Matrizen auf dem Heap, oder muss ich 
diese ebenfalls alle mit new allozieren?

Was ich auch immer nicht genau weiß. Wann macht es insgesamt Sinn 
Speicher mit new zu allozieren und wann ist es besser Daten auf den 
Stack zu packen? Gibt es da eine Faustregel oder so?

von Bernd K. (prof7bit)


Lesenswert?

c_newbie schrieb:
> Wann macht es insgesamt Sinn
> Speicher mit new zu allozieren und wann ist es besser Daten auf den
> Stack zu packen? Gibt es da eine Faustregel oder so?

Wenn die Objekte nicht mehr gebraucht werden sobald die Funktion 
verlassen wird dann kannst Du sie auf den Stack packen.

Wenn sie länger leben sollen (z.B. weil Du zum Beispiel 
Nachrichtenobjekte erzeugst und in eine Queue einreihst oder eine 
verkettete Liste aus Objekten auufbaust wo sie dann längere Zeit 
herumliegen sollen, auch nachdem die Funktion wieder verlassen wurde), 
dann auf dem Heap.

Wenn Du von Anfang an genau weißt daß Du zum Beispiel genau 3 Instanzen 
davon brauchst die von Anfang bis Ende Leben sollen dann kannst Du sie 
auch einfach als globale Variablen anlegen.

Wenn Du auf nem kleinen Microcontroller mit knappem RAM unterwegs bist 
macht es Sinn lange und hart nachzudenken wie man seine Daten so 
organisieren kann daß man komplett auf das dynamische Allozieren von 
Speicher zur Laufzeit verzichten kann.

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

c_newbie schrieb:
> Wann macht es insgesamt Sinn
> Speicher mit new zu allozieren
Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse 
nicht kennst, z.B. wenn du den Inhalt einer Datei lesen willst.
Da weisst du ja beim Programmieren noch nicht, wie gross die Datei sein 
wird, die du mal einlesen willst.
Natuerlich kannst du ein Array anlegen das x GB gross ist, dann solltest 
du aber hoffen, dass die Datei nie groesser sein wird...
Ausserdem nutzt du so unnoetig Speicherplatz, den du vielleicht nie 
brauchst. Also besorgt sich das Programm die Groesse der Datei und 
allokiert soviel Speicher wie noetig.

Wenn du aber immer feste Daten(laengen) bekommst, dann kannst du einfach 
z.B. ein Array anlegen mit x Elementen, da du nie weniger oder mehr 
Speicher benoetigen wirst. Kein Grund fuer dynamische allokation.

von jo (Gast)


Lesenswert?

>Wann macht es insgesamt Sinn
>Speicher mit new zu allozieren und wann ist es besser Daten auf den
>Stack zu packen?

Jo. Dynamisch Speicher allokiert man nicht - da stimmt was mit dem 
Design nicht. Irgendwann stellst du dir die Frage nicht mehr, das ist 
der Zeitpunkt an dem du dann auch dynamisch Speicher allokieren darfst.

gruß J

von Peter II (Gast)


Lesenswert?

Kaj schrieb:
> Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse
> nicht kennst,

ich würde sagen das ist falsch.

Dann müssten man bei einem String ja immer new verwenden müssen.

von Vlad T. (vlad_tepesch)


Lesenswert?

c_newbie schrieb:
> erstelle, befinden sich dann alle Matrizen auf dem Heap, oder muss ich
> diese ebenfalls alle mit new allozieren?

Es wird so viel speicher angefordert, dass das Objekt da mit all seinen 
Membern reinpasst.

Da du die Matrizen als Member und nicht als Pointer drin hast, ist deren 
Speicherbedarf im Specherbedarf von Objekt enthalten.

Was die Matrizen selbst neben ihrer Objektstruktur noch an speicher 
brauchen, müssen sie sich natürlich selbst in ihrem Konstruktor holen, 
der bei Erzeugung deines Objektes natürlich mit aufgerufen wird.

von Bernd K. (prof7bit)


Lesenswert?

jo schrieb:
> Jo. Dynamisch Speicher allokiert man nicht - da stimmt was mit dem
> Design nicht.

Diese Aussage ist in ihrer Pauschalität kompletter Unsinn. Es gibt 
tausend Anwendungen bei denen erst zur Laufzeit bekannt werden wird 
wieviele Instanzen eines Objekts man braucht oder wie groß irgendein ein 
Array werden muss.

von Vlad T. (vlad_tepesch)


Lesenswert?

Peter II schrieb:
> Dann müssten man bei einem String ja immer new verwenden müssen.

was meinst du denn, was in den meisten std::string-Operationen passiert?

von jo (Gast)


Lesenswert?

>Diese Aussage ist in ihrer Pauschalität kompletter Unsinn. Es gibt
>tausend Anwendungen bei denen erst zur Laufzeit bekannt werden wird
>wieviele Instanzen eines Objekts man braucht oder wie groß irgendein ein
>Array werden muss.

Du hast meine Aussage nicht verstanden, hab ich auch nicht erwartet. Und 
stänkern tue ich auch gern. hahahahah

Gruß J

von Peter II (Gast)


Lesenswert?

Vlad T. schrieb:
> was meinst du denn, was in den meisten std::string-Operationen passiert?

es geht nicht warum was in dem Objekt passiert, sondern wo das objekt 
angelegt wird.

von Daniel A. (daniel-a)


Lesenswert?

Jede Variable braucht Speicher. So braucht ein "int" immer genug 
Speicher um eine Zahl zu speichern, und ein "struct{int x;int y;}z;" 
braucht genug Speicher um alle seine Member (also x und y) speichern zu 
können. Auch Pointer brauchen Speicher, nähmlich genug Speicher um die 
Adresse von auf was auch immer sie Zeigen speichern zu können. Wo das 
ist worauf diese zeigen ist eine andere sache. Und natürlich auch ein 
"int x[10];" genug speicher um 10 ints zu speichern. Wenn du jetzt ein 
Struct oder eine Klasse als member eines Structs oder einer Klasse hast, 
oder wirklich beliebige Datentypen, ist der Speicher der der diese 
Felder benötigen ein teil vom Speicher welcher das Strukt benötigt, es 
ist darin enthalten.

Es spielt also nur eine Rolle, was Matrix4 ist und tut. Wärend der 
Speicher der Instanzen der Felder vom typ Matrix4 bei dir teil der 
classe Objekt ist, und somit dort ist, wo auch immer die Instanz der 
Klasse Objekt ist, kann Matrix4 durchaus ein Pointer sein, oder Pointer 
enthalten, die dann sontwohin zeigen könnten. Ob Matrix4 einige Daten im 
Heap ablegt und mit Pointern darauf zeigt, ist der Implementation der 
Matrix4 überlassen.

Als Faustregel, ob man Objekte auf dem Stack oder Heap anlegen sollte, 
würde ich sagen:
 * Wenn man eine Variable nur innerhalb eines Scopes braucht (z.B. 
innerhalb einer Funktion), und darauf keine externen referenzen/pointer 
hat, dann auf dem Stack.
 * Wenn die Anzahl zur Runtime variabel ist, dann auf dem Heap.
 * Wenn man nur eine Referenz/Pointer hat, aber diese bei der 
erstellenden Funktion zurückgiebt, dann auf dem Heap.
 * Wenn man mehrere externe Referenzen/Pointer hat, dann auch auf dem 
Heap, aber das free mittels std::shared erledigen lassen.
 * etc.

von Mark B. (markbrandis)


Lesenswert?

Kaj schrieb:
> c_newbie schrieb:
>> Wann macht es insgesamt Sinn
>> Speicher mit new zu allozieren
> Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse
> nicht kennst, z.B. wenn du den Inhalt einer Datei lesen willst.
> Da weisst du ja beim Programmieren noch nicht, wie gross die Datei sein
> wird, die du mal einlesen willst.

Ich kann aber die Größe der Datei vorher abfragen, jedes Betriebssystem 
unterstützt das. Und ich kann z.B. nur einen Teil der Datei in den 
Speicher einlesen. Es muss nicht unbedingt die ganze Datei auf einmal 
sein. Zum Beispiel bei einer Logdatei interessieren mich vielleicht nur 
die letzten zehn Einträge. Wenn sie nicht eh so eingerichtet ist, dass 
sie zyklisch überschrieben wird und daher eine gewisse Größe gar nicht 
überschreiten kann.

Und falls es kein Betriebssystem gibt, weil z.B. ein kleiner 
Mikrocontroller "bare metal" programmiert, dann wäre es erst recht 
seltsam wenn man es ständig mit beliebig großen Dateien zu tun hätte. 
;-)

: Bearbeitet durch User
von Vlad T. (vlad_tepesch)


Lesenswert?

Peter II schrieb:
> Vlad T. schrieb:
>> was meinst du denn, was in den meisten std::string-Operationen passiert?
>
> es geht nicht warum was in dem Objekt passiert, sondern wo das objekt
> angelegt wird.

Sorry, aber stellst du dich mit Absicht so dumm an?

Das Objekt wird da angelegt, wo du es deklarierst. Trotzdem holt es sich 
intern natürlich Speicher vom Heap, um die Textdaten variabler Länge zu 
managen.
Und wenn durch eine Operation die Länge des geholten Speichers 
überschritten würde, wird vorher neu alloziert und kopiert.
Die Pufferverwaltung ist in der Regel aber so gelöst, dass nicht wegen 
jedem zusätzlichen Zeichen neuer Speicher geholt und umkopiert werden 
muss (es wird einfach mehr als benötigt geholt)

von Peter II (Gast)


Lesenswert?

Vlad T. schrieb:
> Sorry, aber stellst du dich mit Absicht so dumm an?

nein, wie kommst du darauf.

> Das Objekt wird da angelegt, wo du es deklarierst.
nein.

man kann es mit

string s* = new string();

oder mit

string s

anlegen. Dabei spielt es keine Rolle welche länge der String später hat. 
Das war nur die Antwort auf den Kommentar von  Kaj der behauptet das man 
new immer dann verwendet wenn die länge nicht feststeht - und das stimmt 
nicht.

von Vlad T. (vlad_tepesch)


Lesenswert?

Mark B. schrieb:
> Ich kann aber die Größe der Datei vorher abfragen, jedes Betriebssystem
> unterstützt das.

das kannst du aber nicht zur Compile-Zeit. Und wenn du es zur Laufzeit 
machst, musst du genau dann entsprechend der Dateigröße dynamischen 
Speicher allozieren. (es sei denn man legt vorher eine statischen, 
maximale Häppchengröße fest und arbeitet mit dieser.

von Vlad T. (vlad_tepesch)


Lesenswert?

Peter II schrieb:
> man kann es mit
>
> string s* = new string();
genau, hiers sagst du du willst es auf dem Heap

>
> oder mit
>
> string s
und hier auf dem Stack (falls in nem lokalen scope)

Warum stimmt meine Aussage also nicht.

>
> anlegen. Dabei spielt es keine Rolle welche länge der String später hat.
> Das war nur die Antwort auf den Kommentar von  Kaj der behauptet das man
> new immer dann verwendet wenn die länge nicht feststeht - und das stimmt
> nicht.
Kay ging es um die Implementierung. Und genau das macht std::string 
auch. Er verwendet intern dynamischen (=Heap) Speicher, weil die Länge 
des zu haltenden Textes zur Compilezeit von std::string noch nicht fest 
stand.

: Bearbeitet durch User
von Mark B. (markbrandis)


Lesenswert?

Vlad T. schrieb:
> das kannst du aber nicht zur Compile-Zeit. Und wenn du es zur Laufzeit
> machst, musst du genau dann entsprechend der Dateigröße dynamischen
> Speicher allozieren.

Seit wann denn das? Ich kann auch von einer fünf Megabyte großen Datei 
nur die ersten einhundert Bytes einlesen, wenn ich lustig bin.

von Bernd K. (prof7bit)


Lesenswert?

Mark B. schrieb:
> Vlad T. schrieb:
>> das kannst du aber nicht zur Compile-Zeit. Und wenn du es zur Laufzeit
>> machst, musst du genau dann entsprechend der Dateigröße dynamischen
>> Speicher allozieren.
>
> Seit wann denn das? Ich kann auch von einer fünf Megabyte großen Datei
> nur die ersten einhundert Bytes einlesen, wenn ich lustig bin.

Und was machst Du wenn Du aus der Datei dann zum Beispiel nen Syntaxbaum 
oder irgend ne andere Art von Graph oder sonstige Struktur aufbauen 
willst, wo soll das gespeichert werden?

von Mark B. (markbrandis)


Lesenswert?

Bernd K. schrieb:
> Mark B. schrieb:
>> Vlad T. schrieb:
>>> das kannst du aber nicht zur Compile-Zeit. Und wenn du es zur Laufzeit
>>> machst, musst du genau dann entsprechend der Dateigröße dynamischen
>>> Speicher allozieren.
>>
>> Seit wann denn das? Ich kann auch von einer fünf Megabyte großen Datei
>> nur die ersten einhundert Bytes einlesen, wenn ich lustig bin.
>
> Und was machst Du wenn Du aus der Datei dann zum Beispiel nen Syntaxbaum
> oder irgend ne andere Art von Graph oder sonstige Struktur aufbauen
> willst, wo soll das gespeichert werden?

Meine Antwort war dazu gedacht, die Aussage von Kaj (Gast) zu 
wiederlegen:

> Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse
> nicht kennst, z.B. wenn du den Inhalt einer Datei lesen willst.
> Da weisst du ja beim Programmieren noch nicht, wie gross die Datei sein
> wird, die du mal einlesen willst.

"Immer" bzw. das Beispiel stimmt so pauschal eben nicht.

Natürlich hast Du Recht, dass es viele Anwendungsfälle gibt, bei denen 
es so ist wie oben beschrieben. Nur eben nicht immer.

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Mark B. schrieb:
> "Immer" bzw. das Beispiel stimmt so pauschal eben nicht.
>
> Natürlich hast Du Recht, dass es viele Anwendungsfälle gibt, bei denen
> es so ist wie oben beschrieben. Nur eben nicht immer.
Entweder du weisst wie gross deine Daten sind oder nicht.
Du willst x Byte lesen? Dann weisst du die Groesse doch.

Mark B. schrieb:
> Meine Antwort war dazu gedacht, die Aussage von Kaj (Gast) zu
> wiederlegen:
Sorry, aber wiederlegt hast du gar nichts.

Ich schrieb "Immer dann, wenn du [...] die Groesse nicht kennst..."
und du kommst mit "wenn ich eine feste groesse lesen will"...

Tschuldigung, aber auf dein Trollniveau geh ich gar nicht weiter ein.

von tictactoe (Gast)


Lesenswert?

c_newbie schrieb:
> wenn ich jetzt ein Objekt über
> objekt1 = new Objekt(param);
> erstelle, befinden sich dann alle Matrizen auf dem Heap, oder muss ich
> diese ebenfalls alle mit new allozieren?

Also, wenn die Klasse richtig (weniger dogmatisch gesagt: vernünftig) 
implementiert ist, dann brauchst du dir die Frage nicht stellen; du 
wirst nichts weiter tun müssen. Es ist Aufgabe des Konstruktors der 
Klasse, die Matrizen geeignet zu initialisieren.

c_newbie schrieb:
> Wann macht es insgesamt Sinn
> Speicher mit new zu allozieren und wann ist es besser Daten auf den
> Stack zu packen? Gibt es da eine Faustregel oder so?

Faustregel: Wenn die Objekte "Werte" repräsentieren, dann kann man sie 
auf dem Stack anlegen; wenn die Objekte eine "Identität" besitzen, dann 
muss man sie besser auf dem Heap anlagen.

Was heißt das nun? Viele Objekte repräsentieren nur "Werte", z.B., ein 
int, ein String, eine Matrix. Werte können beliebig kopiert werden und 
man kann die Kopien unabhängig voneinander verändern. Der Code wird also 
üblicherweise so aussehen:
1
int dim = 5;
2
std::string text = "abc";
3
Matrix m(dim);
aber nie so:
1
int *dim = new int(5);
2
std::string *text = new std::string("abc");
3
Matrix *m = new Matrix(*dim);

Objekte besitzen eine "Identität", wenn es keinen Sinn macht, die 
Objekte zu kopieren. Wenn dein Objekt z.B. ein Fenster auf dem Desktop 
repräsentiert, dann hat es eine Identität. Ein Fenster wie eine Zahl 
oder einen String zu kopieren macht keinen Sinn. Dann wird man eher
1
Window *wnd = new Window;
schreiben. Ab dann muss man sich aber auch Gedanken machen, wer für das 
so allozierte Objekt zuständig ist und wer es freigeben muss. Weitere 
Beispiele für Objekte mit Identität sind Spieler in einem Spiel, eine 
Datei, eine Datenbanktabelle.

von Mark B. (markbrandis)


Lesenswert?

Kaj schrieb:
> Mark B. schrieb:
>> "Immer" bzw. das Beispiel stimmt so pauschal eben nicht.
>>
>> Natürlich hast Du Recht, dass es viele Anwendungsfälle gibt, bei denen
>> es so ist wie oben beschrieben. Nur eben nicht immer.
> Entweder du weisst wie gross deine Daten sind oder nicht.
> Du willst x Byte lesen? Dann weisst du die Groesse doch.

Was Du übersiehst: Man kann sukzessive Blöcke einer festen Größe lesen, 
um am Ende eine Datei von beliebiger Länge abzuarbeiten. Genau so machen 
es zum Beispiel MP3-Player oder DVD-Player. Die Datei muss zum Abspielen 
nicht komplett in den Hauptspeicher geladen werden, es reicht ein Teil 
davon. Es wäre sonst auch ziemlich schwierig, Audio- oder Videodateien 
mit mehreren Stunden Länge abzuspielen. Es besteht also in diesen Fällen 
keine zwingende Notwendigkeit dafür, dynamisch Speicher zu allokieren. 
Nichtsdestotrotz kann die Datei eine beliebige Länge haben, die zur 
Übersetzungszeit nicht bekannt ist.

Natürlich gibt es auch etliche Anwendungsfälle, in denen man eine Datei 
komplett einlesen muss. Zum Beispiel wenn man ein Foto auf dem 
Bildschirm darstellen will. Das ändert aber nichts daran, dass Deine 
Aussage von weiter oben nicht für alle Anwendungsfälle gilt.

> Mark B. schrieb:
>> Meine Antwort war dazu gedacht, die Aussage von Kaj (Gast) zu
>> wiederlegen:
> Sorry, aber wiederlegt hast du gar nichts.
>
> Ich schrieb "Immer dann, wenn du [...] die Groesse nicht kennst..."
> und du kommst mit "wenn ich eine feste groesse lesen will"...
>
> Tschuldigung, aber auf dein Trollniveau geh ich gar nicht weiter ein.

Fähige Leute tauschen Argumente auf sachlicher Ebene aus. Ich habe Dir 
ein gültiges Gegenbeispiel genannt, das Deine Aussage widerlegt. Nun 
kehre bitte auch wieder auf die sachliche Ebene zurück. Vielen Dank.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Mark B. schrieb:
> Kaj schrieb:
>> Mark B. schrieb:
>>> "Immer" bzw. das Beispiel stimmt so pauschal eben nicht.
>>>
>>> Natürlich hast Du Recht, dass es viele Anwendungsfälle gibt, bei denen
>>> es so ist wie oben beschrieben. Nur eben nicht immer.
>> Entweder du weisst wie gross deine Daten sind oder nicht.
>> Du willst x Byte lesen? Dann weisst du die Groesse doch.
>
> Was Du übersiehst: Man kann sukzessive Blöcke einer festen Größe lesen,
> um am Ende eine Datei von beliebiger Länge abzuarbeiten.

Aber dann weißt du, wie groß die Daten sind, die du lesen willst.

Kaj schrieb:
> Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse
> nicht kennst,

Wenn du aus der Datei 1000 Bytes liest und diese 1000 fest im Quellcode 
eingetragen hast, weißt du zur Compilezeit, dass (maximal) 1000 Bytes 
kommen, also kennst die Größe doch. Laut Kaj brauchst du dafür also 
keinen dynamischen Speicher.

von Mark B. (markbrandis)


Lesenswert?

Rolf M. schrieb:
> Mark B. schrieb:
>> Kaj schrieb:
>>> Mark B. schrieb:
>>>> "Immer" bzw. das Beispiel stimmt so pauschal eben nicht.
>>>>
>>>> Natürlich hast Du Recht, dass es viele Anwendungsfälle gibt, bei denen
>>>> es so ist wie oben beschrieben. Nur eben nicht immer.
>>> Entweder du weisst wie gross deine Daten sind oder nicht.
>>> Du willst x Byte lesen? Dann weisst du die Groesse doch.
>>
>> Was Du übersiehst: Man kann sukzessive Blöcke einer festen Größe lesen,
>> um am Ende eine Datei von beliebiger Länge abzuarbeiten.
>
> Aber dann weißt du, wie groß die Daten sind, die du lesen willst.

Jein. Ich kenne dann die Größe eines zu bearbeitenden Teils, aber nicht 
die Gesamtgröße.

Vielleicht reden wir hier auch aneinander vorbei. Die Aussage war ja:

Kaj schrieb:
> Immer dann, wenn du Daten bekommst, bei denen du vorher die Groesse
> nicht kennst, z.B. wenn du den Inhalt einer Datei lesen willst.

Wovon der erste Teil richtig ist, aber das Beispiel passt so nicht weil 
es wie beschrieben Gegenbeispiele gibt.

von Eric B. (beric)


Lesenswert?

Vlad T. schrieb:
> c_newbie schrieb:
>> erstelle, befinden sich dann alle Matrizen auf dem Heap, oder muss ich
>> diese ebenfalls alle mit new allozieren?
>
> Es wird so viel speicher angefordert, dass das Objekt da mit all seinen
> Membern reinpasst.
>
> Da du die Matrizen als Member und nicht als Pointer drin hast,

Das ist nicht sicher, da die Typ-definition von Matrix4 nicht bekannt 
ist.
Vielleicht ist es definiert als ein pointer auf einem Struct.

von Vlad T. (vlad_tepesch)


Lesenswert?

Eric B. schrieb:
> Das ist nicht sicher, da die Typ-definition von Matrix4 nicht bekannt
> ist.
> Vielleicht ist es definiert als ein pointer auf einem Struct.

dann hat er es nicht anders verdient *diabolisch lach* 

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.