Operationen mit mehreren Klassen
-
Hallo leute,
ich weiß, ich sollte bei Fragestellungen auch selber Ansätze liefern. Aber bei dieser Aufgabe habe ich echt kein Schimmer was überhaupt los ist. Könnte mir bitte jemand erklären was zur Hölle hier vor sich geht.
# include<iostream> using namespace std; class X { public: X () {cout<<"KX"<<endl;} ~ X () {cout<<"DX"<<endl; } }; class Y { private: X x [2][1]; public: Y () {cout<<"KY"<<endl;} ~ Y () { cout<<"DY"<<endl; } }; class Z { private: Y y; X x; public: Z () {cout<<"KZ"<<endl;} ~ Z () { cout<<"DZ"<<endl; } }; int main () { {Y y; X x; } Z*ZPtr= new Z [2]; system ("pause") ; return 0; }ich wenigen Tagen ist klausurtime und ich häng seit 2 Tagen an dieser Altklausuraufgabe. Bitte erklärt mir mal was da passiert
Ausgabe: KX KX KY KX DX DY DX DX KX KX KY KX KZ KX KX KY KX KZ
-
Da werden ein paar Objekte erzeugt und (automatisch) zerstört, wobei im Konstruktor und Destruktor jeweils was ausgegeben wird. Könntest du etwas konkreter fragen? Konstruktor und Destruktor sollte dir etwas sagen, von daher kann es nicht sein, dass du mit der Ausgabe absolut gar nichts anfangen kannst.
-
Ich rate mal, was du wissen möchtest: Die Member einer Klasseninstanz werden erzeugt, bevor der Körper des Konstruktors ausgeführt wird. Daher bekommst du beim
Y y;ganz am Anfang der main erst einmal zweimal "Kx" als Ausgabe, wenn die beiden X im Y erzeugt werden.Beim Destruktor läuft dann alles in genau umgekehrter Reihenfolge ab.
-
Heute scheint Quiz-Tag zu sein. Aber S.lukas ist wohl schon im Bett.
-
Ich denke die Reihenfolge der Ausgabe verwirrt dich:
Die Standardinitialisierung der Variablen einer Klasse werden vor dem Rumpf des Konstruktor initialisiert.
Wenn ein Block verlassen wird, wird der Destruktor von den Variablen aufgerufen, die im selbigen Block deklariert werden.Falls du von Java kommst: In C++ wird kein
newbenötigt, um Variablen zu erstellen (Heap ist eine andere Geschichte).
Damit solltest du dir eigentlich die Ausgabe erklären können
Edit: Mist, zu langsam

-
ja natürlich, mit den Konstruktoren kann ich was anfangen. KX,KY..usw ist klar. Aber was bestimmt jetzt genau, in welcher Reihenfolge die dinger ausgegeben werden. Also ich weiß nicht so recht wann sich das Programm in welcher Klasse befindet. dieses
Y y und
X x [2][1] was ist das ? Dieses Große X und Y steht für die KLasse aber die Kleinbuchstaben ? Wenn das Variablen sind, wo genau erfolg die deklaration ?
-
vatan89 schrieb:
Wenn das Variablen sind, wo genau erfolg die deklaration?
das ist die deklaration, was du meinst ist die definition, welche hier nicht benötigt wird. Die Variablen werden mit dem Standardkonstruktor aufgerufen
vatan89 schrieb:
X x [2][1]
Die eckigen Klammern sind Array-Klammern
-
// Füge Headerdatei ein #include <iostream> // std::cout // Benutze Namensraum std / verwerfe das nötige Voranstellen von std:: using namespace std; // Definiere Klasse X class X{ public: // Ctor & Dtor X () {cout<<"KX"<<endl;} ~ X () {cout<<"DX"<<endl; } }; // Definiere Klasse Y class Y{ private: // Array mit 2x1 Instanz der Klasse X X x [2][1]; public: // Ctor & Dtor Y () {cout<<"KY"<<endl;} ~ Y () { cout<<"DY"<<endl; } }; // Definiere Klasse Z class Z{ private: // Instanz der Klasse Y Y y; // Instanz der Klasse X X x; public: // Ctor & Dtor Z () {cout<<"KZ"<<endl;} ~ Z () { cout<<"DZ"<<endl; } }; // Definiere Programm-Einstiegsfunktion int main (){ // Code-Block (Scope kann ausgenutzt werden, um temporäre Objekte automatisch wieder zerstören zu lassen) { // Erstelle Instanz der Klasse Y Y y; // Erstelle Instanz der Klasse X X x; } // Erstelle dynamisch ein Array mit 2 Instanzen der Klasse Z Z* ZPtr = new Z[2]; // Führe System-Befehl "pause" aus / warte auf Eingabe system ("pause"); // Beende Program mit Rückgabewert 0 (gebräuchliche Status-Angabe für 'alles in Ordnung') return 0; }KX <-- Zeile 45: Erstellung von erstem X in Y = Konstruktor von X
KX <-- Zeile 45: Erstellung von zweitem X in Y = Konstruktor von X
KY <-- Zeile 45: Konstruktor von Y
KX <-- Zeile 47: Konstruktor von X
DX <-- Zeile 48: Verlassen des Scopes = Zerstörung von x = Destruktor von X
DY <-- Zeile 48: Verlassen des Scopes = Zerstörung von y = Destruktor von Y
DX <-- Zeile 48: Verlassen des Scopes = Zerstörung von y = Zerstörung des ersten X in Y = Destruktor von X
DX <-- Zeile 48: Verlassen des Scopes = Zerstörung von y = Zerstörung des zweiten X in Y = Destruktor von X
KX <-- Zeile 50: Erstellung von erstem Z = Erstellung von Y in Z = Erstellen von erstem X in Y in Z = Konstruktor von X
KX <-- Zeile 50: Erstellung von erstem Z = Erstellung von Y in Z = Erstellen von zweitem X in Y in Z = Konstruktor von X
KY <-- Zeile 50: Erstellung von erstem Z = Erstellung von Y in Z = Konstruktor von Y
KX <-- Zeile 50: Erstellung von erstem Z = Erstellung von X in Z = Konstruktor von X
KZ <-- Zeile 50: Erstellung von erstem Z = Konstruktor von Z
KX <-- Zeile 50: Erstellung von zweitem Z...
KX <-- ...
KY <-- ...
KX <-- ...
KZ <-- ... (selbe wie beim ersten)Das Array in Zeile 50 wurde dynamisch erstellt, also zur Laufzeit, und wird daher nicht automatisch zerstört, deswegen findet keine Zerstörung einer Instanz von Z statt.
-
Ich werfe mal das Stichwort "Initialisierungsliste" in den Raum, damit wird das evtl. etwas klarer:
class X { public: X() : m_int(5), m_y() {} private: int m_int; Y m_y; }Die Initialisierungsliste kann weggelassen werden, dann werden alle Member als default initialisiert (sofern dies möglich ist, ansonsten gibts ne Fehlermeldung).
Somit sollte auch klar sein, dass die Member initialisiert werden, *bevor* der Rumpf des Konstruktors aufgerufen wird.
-
KX //Durch das X x [2][1] wird das KX zweimal ausgegeben KX // KY //Anschließend tritt der Konstruktor von Class Y KX //und von der Class X DX // Destruktor von Class X DY //Weshalb kommt hier erst der Destruktor von Y ? DX // ICh schätze mal die beiden DX killen die beiden KX am Anfang DX KX // hier beginnt die Instanz der Klasse X x ? KX KY KX // Ende von der Instanz Xx ? KZ KX KX KY KX // gleiche Frage wie oben. Warum kommt hier nochmal der Konstruktor von X KZ
-
vatan89 schrieb:
KX //Durch das X x [2][1] wird das KX zweimal ausgegeben KX // KY //Anschließend tritt der Konstruktor von Class Y KX //und von der Class X DX // Destruktor von Class X DY //Weshalb kommt hier erst der Destruktor von Y ? DX // ICh schätze mal die beiden DX killen die beiden KX am Anfang DX KX // hier beginnt die Instanz der Klasse X x ? KX KY KX // Ende von der Instanz Xx ? KZ KX KX KY KX // gleiche Frage wie oben. Warum kommt hier nochmal der Konstruktor von X KZAufräumen geschieht genau umgekehrt wie das Erstellen.
int main() { { Y y;Ein Y wird erstellt. Es enthält 2 X. Als Member werden diese zuerst erstellt:
KX KXDann der Rest vom Y selbst:
KYX x;Ein X wird erstellt:
KX}Ende des Scopes, das Y und das X werden zerstört. Zuerst das X:
DXDann das Y:
DYDann die beiden X, die Member von Y sind:
DX DXZ*ZPtr= new Z [2];Zwei Z werden erzeugt. Nacheinander. Jedes Z enthält ein Y und ein X (das Y zuerst, da es weiter oben steht). Das Y wieder zwei X. Also zuerst wird das Y erzeugt, dazu werden zuerst zwei X erzeugt:
KX KXDann das Y selbst:
KYDann das X als zweiter Member des Z:
KXDann das Z selbst:
KZann das gleiche noch einmal für das zweite Z:
KX KX KY KX KZPreisfrage, ob du es auch verstanden hast: Wie sähe die Ausgabe bei einem anschließenden
delete[] ZPtr;aus? (Mit Erklärung, nicht einfach ausprobieren!)
-
poah Wahnsinn danke Leute, echt. Hab's jetzt endlich kapiert *schwere Geburt*

Also zur Preisfrage:
Wenn ich den Pointer Kille :
DZ DX DY DX DX DZ DX DY DX DX???? so in der Art
-
Fast. y wird in Z zuerst deklariert, also auch zuerst zerstört.
Tauscch die Zerstörung von y und x aus, dann stimmt's.
-
Ethon schrieb:
Fast. y wird in Z zuerst deklariert, also auch zuerst zerstört.
Tauscch die Zerstörung von y und x aus, dann stimmt's.Nein, gerade aus diesem Grund ist es so wie vatan89 richtig gesagt hat.
-
Ethon schrieb:
Fast. y wird in Z zuerst deklariert, also
auch zuerst zerstört.zuletzt zerstört.
Zerstört wird immer in der umgekehrten Reihenfolge wie aufgebaut wurde.
-
Grrr, in letzter Zeit häufen sich Griffe ins Klo bei mir. Vor allem wenn ich eigentlich wüsste wie es richtig ist.
Ich schiebe es mal auf Müdigkeit beim Erstellen des Posts und entschuldige mich für eventuell entstandene Verwirrung.
-
Leute hab grad die Klausur hinter mir und dazu eine dringende Frage, was ich unbedingt wissen muss:
In der Aufgabe kam sowas ähnliches wie ich hier schon gepostet habe.
es sah in der Form aus, solang es noch frisch ist:
3- Klassen jeweils x,y,z
f1(....)
{ Z = new y} // oder so ähnlich, aufjedenfall ein new Objektint main () { Y y; // so in der Form die Funktion f1 wurde in der Klasse beschrieben f1(y); }Meine Frage, befindet sich das f1(y) noch im Scope ? sprich wenn ich die Konstruktoren erzeuge.
KX | KX | gehören alle dem Objekt Yy KY | KX |werden diese vor dem übergang zu f1(y) zerstört oder erst nach der ausführung des von f1(y) bzw des new y ?
falls ich es etwas unklar formuliert habe, entschuldigt dies bitte es ist nur eine wage beschreiben. Ich hatte eine wahl zwischen
KX KX KX KX KY KY KX KX KZ oder KZ KX DZ KX DX KY DY KX DX KZ DX DZ KX DX KX DY KY DX KX DX KZich habe dummerweise das linke gewählt, weil ich dachte, dass zuerst new noch erstellt wird und dann das objekt von oben zerstört wird. Aber ich denke dass das rechte richtig ist.
Findet nacht der main die zerstörung vor dem new statt oder erst nachdem new erstellt wurde ?
-
Der Scope endet mit der schließenden Klammer. Zu dem Zeitpunkt werden auch die lokalen Variablen zerstört.
Eine Funktion hat ihren eigenen Scope. Du kannst es dir so vorstellen, dass der Rumpf der Funktion an der Stelle des Aufrufs eingefügt wird (was bei inline-Methoden tatsächlich der Fall ist)
Also sind bezüglich der Objekt-Lebensdauer äquivalent:
f() { X x = new X; } int main() { Y y; f(); }und
int main() { Y y; { X* x = new X; } }Da das innere x aber nicht zerstört wird, ist es eigentlich auch egal, wie lange es genau lebt

-
Ein Scope wird verlassen, wenn seine geschweifte Klammer geschlossen wird:
{ Y y; // Code { // Code { // usw. } // interessiert y nicht die Bohne // Code } // interessiert y nicht die Bohne // Code } // Hier stirbt y.Dazwischen mag stehen was will. Selbstverständlich auch Funktionsaufrufe. Auch bei einer Exception würde y erst an der beschriebenen Stelle zerstört. Ebenso bei einem goto, break oder ähnlichem aus dem Scope heraus. Einzig solch gefürchtete Mittel wie exit, abort, longjmp & Co. können den Scope verlassen, ohne dass ein Destruktor aufgerufen wird (und sind daher zu vermeiden, wenn man nicht ganz genau weiß, was man tut).
daddy_felix schrieb:
int main() { Y y; { X* x = new X; } }Da das innere x aber nicht zerstört wird, ist es eigentlich auch egal, wie lange es genau lebt

Das x wird schon zerstört. Aber x ist hier ein roher Pointer und sein Pointee bekommt davon nichts mit. Das mit new erzeugte Objekt existiert also immer noch und ist nicht mehr erreichbar. Typisches Anfängerspeicherloch, wie bei bisher jedem Code den vatan89 uns von seinem Lehrer gezeigt hat.
-
also ist die rechte Variante richtig ? Da lag ich mit der linken wohl wieder voll auf dem Holzweg...wo soll das nur enden mit mir
-
vatan89 schrieb:
also ist die rechte Variante richtig ?
Nein, alles ist falsch. Oder wahrscheinlicher: Du erinnerst dich falsch an die Aufgabenstellung.