C++: Was landet genau im Heap?

Hallo!

Es mag ein wenig Blöd erscheinen, aber mich würde doch einmal interessieren, was genau im Heap landet, wenn ich folgendes Beispiel betrachte:

class Klasse1 {
 public:
 Klasse1(); //constructor
 int data[12]; //daten
};

....

class Klasse2 {
 public:
 Klasse2(); //constructor
 ~Klasse2(); //destructor
 Klasse1 \*datensatz; //daten
};
Klasse2::Klasse2() 
{
 datensatz = new Klasse1();
}
Klasse2::~Klasse2() 
{ 
 delete datensatz; 
}

Sofern ich denn alles korrekt verstanden habe, definiert Klasse1 ein 128 int umfassendes Array auf dem Stack.
Klasse2 wiederum greift auf Klasse1 zu und (da new verwendet wird) packt dies in einen reservierten Bereich im Heap.

Landen jetzt auch alle Daten von Klasse1 im Heap?
Oder wie sieht die Speicherverteilung aus?
Ist diese Art der strukturierung überhaupt sinnvoll, wenn alle Daten aus Klasse1 schließlich und endlich im Heap abgelegt werden sollen?

Grüße

Björn

Sofern ich denn alles korrekt verstanden habe, definiert
Klasse1 ein 128 int umfassendes Array auf dem Stack.

Ich nehme mal an, dass du dich im Code vertippt hast und es da „int data[128]“ heißen soll. Grundsätzlich wird durch die Definition nur festgelegt, wieviel Speicherplatz für eine Instanz der Klasse benötigt wird. Ob die Daten jetzt auf dem Stack oder im Heap landen liegt da noch nicht fest.

Nebenbei: solange du keine POD-Typen hast, kannst du über die Speicherbelegung außer einer unteren Grenze nicht viel sagen! Deine Klasse2 hat sowohl einen Konstruktor als auch einen virtuellen Destruktor, ist also kein POD-Typ.

Klasse2 wiederum greift auf Klasse1 zu und (da new verwendet
wird) packt dies in einen reservierten Bereich im Heap.

Erst zu dem Zeitpunkt, an dem eine Instanz erzeugt wird, wird der Speicher zugewiesen. In deinem Beispiel geschieht das zur Laufzeit per new, es wird also ein Bereich auf dem heap reserviert. Der andere Fall wäre zur Compile-Zeit (blöde Übersetzung…), wobei der Compiler ausreichend Platz auf dem Stack reserviert und zu einem gegebenen Zeitpunkt den Konstruktor da rüberfahren lässt. Ein dritter fall sind statische Variablen, die in einem eigenen Segment landen und garnichts mit Stack oder Heap am Hut haben.

Landen jetzt auch alle Daten von Klasse1 im Heap?

„Die Datenstruktur, durch die ein Objekt vom Typ Klasse 1 definiert ist“ landet auf dem Stack, also vereinfacht ausgedrückt alle nicht-statischen Attribute. Wenn du Methoden des Objekts aufrufst, dann arbeiten die natürlich genauso mit einem Stack wie alle anderen Funktionen auch. Lediglich „this“ zeigt auf den Heap.

Ist diese Art der strukturierung überhaupt sinnvoll, wenn alle
Daten aus Klasse1 schließlich und endlich im Heap abgelegt
werden sollen?

Wenn Klasse2 lediglich ein Attribut vom Typ Klasse1 haben soll, dann kannst du genausogut auch sowas verwenden:

class Klasse2 {
 public:
 Klasse2(); //constructor
 ~Klasse2(); //destructor
 Klasse1 datensatz; //daten
};

Klasse2::Klasse2() :
 datensatz() // bei einem default-Konstruktor überflüssig
{}

Das erspart dir auch die Arbeit im Destruktor. Der Heap ist hauptsächlich dann praktisch, wenn erst zur Laufzeit zB die Größe eines Arrays bekannt wird, oder wenn Datenstrukturen benötigt werden, die nicht auf den Stack passen. Wenn du „datensatz“ auf jeden Fall komplett auf dem Heap haben willst, dann musst du natürlich per new rumhandwerken. Aber es wäre in dem Fall „sauberer“, Klasse1 das interne Array zur Laufzeit zu erzeugen und selber wieder zu entsorgen. Dann landen zwar immernoch ein paar Daten auf dem Stack, aber das sollte kein Problem sein.

Hallo!

Es mag ein wenig Blöd erscheinen, aber mich würde doch einmal
interessieren, was genau im Heap landet…

Hallo Björn,

es steht dem Compiler (genauer gesagt natürlich seinem Programmierer) in vielen Fällen frei, wie er den Speicher organisiert. Es hat sich beispielsweise eingebürgert, dass Parameter für Unterprogramme auf dem Stack übergeben werden (es geht allerdings auch anders, z.B. über Register), aber viele Compiler legen ab einer bestimmten Datengrösse eine Kopie im Heap an und übergeben die Adresse. Die Compilerbauer entwickeln ihre Produkte natürlich ständig weiter und optimieren auch ganz besonders die Speicherverwaltung. Vorwitzige Programmierer sollten da nicht die Nase hineinstecken, solange es nicht sein muss.

Was wo gespeichert wird, lässt sich daher immer nur für eine ganz bestimmte Compilerversion feststellen, indem man die Dokumentation dazu liest. Die Parameter-Übergabe und Ergebnis-Rückgabe z.B. müssen genau dokumentiert sein, sonst kann man den Compiler nicht zusammen mit anderen Sprachen einsetzen (z.B. Unterprogramm in Assembler). Es könnte natürlich sein, dass der Hersteller darauf keinen Wert legt.

Gruss Reinhard