Constructor

hallo,

hab mir schon ein paar seiten im netz zu diesem begriff angeschaut aber leider hats noch nicht geklingelt - was ein constructor wirklich ist und welche aufgabe er hat.

Ich weiß nur dass eine Klasse einen Constructor besitzt, der den gleichen Namen wie die Klasse haben muss. Und man kann im Constructor Variablen auf definierte Werte setzen etc.

Aber was es bedeutet, dass ein Constructor ausgeführt wird, wenn die Klasse als Objekt instanziert wird, weiß ich leider nicht. Vielleicht kann mir jmd. diesen Sachverhalt an einem bsp näherbringen.

patrick

Hallo patrick,

Ich weiß nur dass eine Klasse einen Constructor besitzt, der
den gleichen Namen wie die Klasse haben muss. Und man kann im
Constructor Variablen auf definierte Werte setzen etc.

Aber was es bedeutet, dass ein Constructor ausgeführt wird,
wenn die Klasse als Objekt instanziert wird, weiß ich leider
nicht. Vielleicht kann mir jmd. diesen Sachverhalt an einem
bsp näherbringen.

Beim Compilieren ist ja nicht bekannt, wieviele Objekte zur Laufzeit benötigt werden, also kann der Compiler für die Objekte auch noch keinen Speicherplatz reservieren.

Also hat man den Heap. Das ist einfach ein Speicherbereich, von welchem man sich Scheibchen abschneiden kann um seine Daten zu verwalten.

Der Constructor mach nun nichts anderes als sich eine passende Speicherscheibe vom Heap zu organisieren und somit den Speicherplatz für sein Objekt anzulegen und die Initialisierung des Objekts durchzuführen.
Der Destructor löst dann alle Verbindungen und gibt den Speicher wieder frei.

MfG Peter(TOO)

Beim Compilieren ist ja nicht bekannt, wieviele Objekte zur
Laufzeit benötigt werden, also kann der Compiler für die
Objekte auch noch keinen Speicherplatz reservieren.

Also hat man den Heap. Das ist einfach ein Speicherbereich,
von welchem man sich Scheibchen abschneiden kann um seine
Daten zu verwalten.

In welcher Beziehung steht der Heap zum Stack? D.h. der Heap ist nicht wie der Stack aufgebaut, sondern man kann zur Laufzeit auf alle Speicherstellen drauf zugreifen… und es wird zur Laufzeit immer nachgeschaut wo noch freie stellen im speicher sind. Der Stack wird ja von Ende des Speichers nach vorne belegt… und der Heap befindet sich somit im vorderen bereich des speichers… ok

Der Constructor mach nun nichts anderes als sich eine passende
Speicherscheibe vom Heap zu organisieren und somit den
Speicherplatz für sein Objekt anzulegen und die
Initialisierung des Objekts durchzuführen.

D.h. auch wenn im Constructor nichts drinnen steht, weiß er wieviel speicher zu reservieren ist? Hmm und das Wort Objekt muss ich für mich noch irgendwie definieren; bis jetzt kann ich mir darunter noch nichts genaues vorstellen. Wieviel speicher muss ich für welches Objekt investieren?

patrick

Hallo patrick,

In welcher Beziehung steht der Heap zum Stack?

Eigentlich in gar keiner :wink:

D.h. der Heap
ist nicht wie der Stack aufgebaut, sondern man kann zur
Laufzeit auf alle Speicherstellen drauf zugreifen…

Nicht ganz so !!
Du darfst nur auf diese Speicherstellen zugreifen, welche dir auf Anfrage hin zugeteilt wurden.
Wie er genau aufgeteilt ist und verwaltet wird, hängt vom Betriebssystem ab.
Die Grundfunctionen sind malloc() und free().
new und delete greifen auf diese Functionen zurück.

und es
wird zur Laufzeit immer nachgeschaut wo noch freie stellen im
Speicher sind. Der Stack wird ja von Ende des Speichers nach
vorne belegt… und der Heap befindet sich somit im vorderen
bereich des speichers… ok

Das hängt wiederum von der CPU ab. Es gibt, bzw. gab, auch CPUs bei denen der Stack in die andere Richtung wächst.

D.h. auch wenn im Constructor nichts drinnen steht, weiß er
wieviel speicher zu reservieren ist?

Ja :wink:

Hmm und das Wort Objekt
muss ich für mich noch irgendwie definieren; bis jetzt kann
ich mir darunter noch nichts genaues vorstellen.

Bei der alten klassischen Programmierung mit z.B. Spaghetti-BASIC gab es nur globale Variablen, d.h. jede aus jeder Codezeile im ganzen Programm konnte immer auf alle Variablen zugegriffen werden. Unterprogramme musste mit GOSUB aufgerufen werden und die Variablennamen waren auf maximal 2 Zeichen begrenzt.
Da hat dann schon einmal ein Unterprogramm die falsche Variable verändert …
Typischer Fall:
Du hast eine FOR NEXT Schleife, welche I als Index-Variable verwendet. Aus dieser Schleife ruftst du ein Unterprogramm auf, welches auch eine Schleife enthält, welche I verwendet … Tja, dann ging die Sucherei nach dem Bug los …

Mit den Prozeduralen Sprachen, kamen dann lokale Variablen hinzu.
Du kannst also in jeder Funktion I als Index-Variable verwenden und diese sind voneinander getrennt, wen sie lokal definiert sind.
Desweiteren kannst du eigene Datentypen definieren. Du kannst also einzelne Variablen welche logisch zusammengehören auch programmtechnisch zu einem Dings … also Objekt … zusammenfassen.

Bei OO geht man einfach noch einen Schritt weiter.
Zu dem Daten-Konstrukt, packt man einfach auch noch alle Funktionen welche es bearbeiten sollen hinzu. Desweiteren kann man dann auch noch angeben welche Elemente nach aussen hin sichbar sind. Die Daten werden also gekapselt.
Zur Übersetzungszeit sind aber nur die Struktur der Daten und die Funktionen bekannt.
Zur Laufzeit muss dann für jedes Objekt erst noch der Speicher für die Variablen und der Verweis auf die darauf anwendbaren Funktionen erstellt werden, was „new“ übernimmt.

Wieviel
speicher muss ich für welches Objekt investieren?

Das hängt von deinem Objekt, dem Compiler und der CPU ab.

MfG Peter(TOO)

Hallo Peter!

Da werden aber jetzt einige Dinge durcheinandergebracht! Der Constructor ist NICHT dafür da, dass zur Laufzeit Speicher für das Objekt reserviert wird. Der Compiler weiß zur Compiletime sehr wohl (zumindest in C++, in python z.b. nicht), wie groß das Objekt ist. Beispiel: Dein Objekt ist eine lokale Variable in einer Funktion. Dann liegt diese auf dem Stack, gleich unter der Adresse, die in %ebp (gnu-assembler-syntax) liegt. Wie soll da dann der Platz berechnet werden, es muss ja zur Compile-time festliegen!

Der Constructor hat folgende Aufgabe:
Er bietet eine Möglichkeit bei der Erzeugung eines Objekts dieses zu initialisieren. Das kann heißen, dass bestimmte Werte gesetzt werden oder aber auch dynamisch Speicher erzeugt wird, auf dem dann eine Membervariable (vom Typ eines Pointers) hinzeigt.

Destructor:
Analog. Dort kann man z.b. den speicher wiederfreigeben oder sonstige Aufräumarbeiten machen.

Bezüglich Heap/Stack:
Der Stack ist eine Datenstruktur die von hohen Adressen (etwa 0xffffffff, ein wenig weniger) zu niedrigen Adressen wachst. Er wird verwendet um Zwischenergebnisse zu speichern, lokale Variablen, Parameter einen Platz zuverschaffen und die Rücksprungadresse und ein Backup des %ebp zu merken.

Der Heap:
Liegt meist über dem .data Segment (.bss also) und wird verwendet um dynamisch Speicher vom Betriebssystem anzufordern. (Genauergesagt wird die obere Addressgrenze verschoben, die Organisation im Heap geschieht im Userspace). Mit anderen Worten: Wenn du in C malloc, realloc, free, new, delete oder dergleichen aufrufst, dann wird am Heap operiert.

so long,
KoRn

Hallo KoRn,

Da werden aber jetzt einige Dinge durcheinandergebracht! Der
Constructor ist NICHT dafür da, dass zur Laufzeit Speicher für
das Objekt reserviert wird. Der Compiler weiß zur Compiletime
sehr wohl (zumindest in C++, in python z.b. nicht), wie groß
das Objekt ist. Beispiel: Dein Objekt ist eine lokale Variable
in einer Funktion. Dann liegt diese auf dem Stack, gleich
unter der Adresse, die in %ebp (gnu-assembler-syntax) liegt.
Wie soll da dann der Platz berechnet werden, es muss ja zur
Compile-time festliegen!

Ich habe nirgends geschrieben, dass der Compiler die Grösse nicht kennt. Er weiss nur nicht wann die Objekte angelegt werden.

Bei Constructor und Destructor hast du recht, da habe ich geschlampt.

Bezüglich Heap/Stack:
Der Stack ist eine Datenstruktur die von hohen Adressen (etwa
0xffffffff, ein wenig weniger) zu niedrigen Adressen wachst.
Er wird verwendet um Zwischenergebnisse zu speichern, lokale
Variablen, Parameter einen Platz zuverschaffen und die
Rücksprungadresse und ein Backup des %ebp zu merken.

Wie man merkt, kennst du nur eine CPU-Modellreihe von Intel.

Das der Stack von oben nach unten wächst, hatte nur den Grund einer besseren Ausnutzung des RAM. Die Idee war, dass der Stack an der höchsten RAM-Adresse begint. An der niedersten RAM-Adresse werden die statichen Variablen abgelegt. Hinter (also über) den statischen Variablen beginnt dann der Heap. Dadurch kann man die Grenze zwischen Obergrenze Heap und Untergrenze Stack zur Laufzeit anpassen.

Der 6502 hatte nur einen 8-Bit Stackpointer und die oberen 8-Bit waren auf 0x01 hardwaremässig festgelegt.
Es gab auch CPUs beiden der Stack nur 4 oder 8 Einträge aufnehmen konnte und direkt in der CPU integriert war, also gar nicht dem normalen Adressraum zugeordent ist. Bei diesen CPUs verbietet sich die Parameterübergabe über den Stack.
Dann gibt es Sprachkonzepte bei denen getrennte Stacks für Rücksprungadressen und Daten verwendet werden, wie z.B. FORTH. Für Forth gibt es spezielle CPUs, welche auch physikalisch eigenen Busse für die Stacks verwenden.

MfG Peter(TOO)

Hallo Peter.

Bezüglich Heap/Stack:
Der Stack ist eine Datenstruktur die von hohen Adressen (etwa
0xffffffff, ein wenig weniger) zu niedrigen Adressen wachst.
Er wird verwendet um Zwischenergebnisse zu speichern, lokale
Variablen, Parameter einen Platz zuverschaffen und die
Rücksprungadresse und ein Backup des %ebp zu merken.

Wie man merkt, kennst du nur eine CPU-Modellreihe von Intel.

Nun gut. Wenn ein Neuanfänger in einem C++ Board schreibt, gehe ich mal davon aus, dass er auf seinem PC programmiert. Und da dürfte man dann wohl mit etwa 98%iger Wahrscheinlichkeit davon ausgehen *g.

Aber der Schluss, dass ich nur die IA-32 Prozessoren kenne ist auch nicht richtig. Ich kenne aber keine Prozessoren, bei denen der Stack sich konzeptionell anders verhält, diese aber ein Konzept für Virtual Memmory anbieten (etwa Paging). Da du da breitere Erfahrung hast, würde mich interessieren, ob du mir einen nennen kannst.

Ich wollte natürlich niemanden beleidigen.

schönen Sonntag,
KoRn