Antworten
Content |
Hier sind die Antworten zu den einzelnen Fragen:
-
Das erste Beispiel ist nicht zulässig. X hat keinen default constructor, da ein Konstruktor mit einem int-Parameter deklariert wurde. In Folge davon hat auch Y keinen default constructor, weil die Variablenkomponente x keinen solchen hat. Der Übersetzer stört sich dann an der Deklaration von „Y y;“, da hierfür ein default constructor benötigt wird, den es nicht gibt.
class X { X(int) {}; // Konstruktor mit einer ganzen Zahl, die ignoriert wird }; class Y { X x; }; Y y;
theon$ g++ -Wall -c case01.cpp case01.cpp:7:3: error: use of deleted function 'Y::Y()' 7 | Y y; | ^ case01.cpp:4:7: note: 'Y::Y()' is implicitly deleted because the default definition would be ill-formed: 4 | class Y { | ^ case01.cpp:4:7: error: no matching function for call to 'X::X()' case01.cpp:2:4: note: candidate: 'X::X(int)' 2 | X(int) {}; // Konstruktor mit einer ganzen Zahl, die ignoriert wird | ^ case01.cpp:2:4: note: candidate expects 1 argument, 0 provided case01.cpp:1:7: note: candidate: 'constexpr X::X(const X&)' 1 | class X { | ^ case01.cpp:1:7: note: candidate expects 1 argument, 0 provided case01.cpp:1:7: note: candidate: 'constexpr X::X(X&&)' case01.cpp:1:7: note: candidate expects 1 argument, 0 provided theon$
-
Das zweite Beispiel ist zulässig, führt aber zu einer Warnung des Übersetzers, da im default constructor von Z die Reihenfolge der Konstruktionen von der Reihenfolge der entsprechenden Deklarationen abweicht. Es gilt aber immer letztere Reihenfolge, so dass zuerst X konstruiert wird, gefolgt von Y:
#include <iostream> class X { public: X() { std::cout << "X constructed" << std::endl; } }; class Y { public: Y() { std::cout << "Y constructed" << std::endl; } }; class Z { public: Z() : y(), x() {} private: X x; Y y; }; int main() { Z z; }
theon$ g++ -Wall -o case02 case02.cpp case02.cpp: In constructor 'Z::Z()': case02.cpp:16:9: warning: 'Z::y' will be initialized after [-Wreorder] 16 | Y y; | ^ case02.cpp:15:9: warning: 'X Z::x' [-Wreorder] 15 | X x; | ^ case02.cpp:13:7: warning: when initialized here [-Wreorder] 13 | Z() : y(), x() {} | ^ theon$ ./case02 X constructed Y constructed theon$
-
Das dritte Beispiel ist nicht zulässig, da konstante Variablendeklarationen nur dann per default constructor konstruiert werden können, wenn diese einen expliziten besitzen. Somit entfällt bei X der default constructor und die Deklaration X x; führt entsprechend zu einem Fehler. Zudem gibt der Übersetzer eine Warnung, dass X in dieser Form nicht sinnvoll ist.
class X { const int i; }; X x;
theon$ g++ -Wall -c case03.cpp case03.cpp:2:14: warning: non-static const member 'const int X::i' in class without a constructor [-Wuninitialized] 2 | const int i; | ^ case03.cpp:5:3: error: use of deleted function 'X::X()' 5 | X x; | ^ case03.cpp:1:7: note: 'X::X()' is implicitly deleted because the default definition would be ill-formed: 1 | class X { | ^ case03.cpp:1:7: error: uninitialized const member in 'class X' case03.cpp:2:14: note: 'const int X::i' should be initialized 2 | const int i; | ^ theon$
Die Sache kann behoben werden, indem die mit const deklarierte Variable explizit konstruiert wird:
class X { public: X() : i(0) { } private: const int i; }; X x;
-
Das vierte Beispiel:
#include <iostream> class X { public: X() { std::cout << "X constructed" << std::endl; } ~X() { std::cout << "X destructed" << std::endl; } }; class Y { X x; public: Y() { std::cout << "Y constructed" << std::endl; } ~Y() { std::cout << "Y destructed" << std::endl; } }; class Z { Y y; public: Z() { std::cout << "Z constructed" << std::endl; } ~Z() { std::cout << "Z destructed" << std::endl; } }; int main() { X x; Y y; Z z; }
theon$ g++ -Wall -o case04 case04.cpp theon$ ./case04 X constructed X constructed Y constructed X constructed Y constructed Z constructed Z destructed Y destructed X destructed Y destructed X destructed X destructed theon$