Order of calling constructors during inheritance. Inheritance restrictions. Properties of a pointer (reference) to the base classThis topic is a continuation of the topic: Show
Contents
Search other resources: 1. Calling class constructors during inheritanceIf two classes form an inheritance hierarchy, then when an instance of a derived class is created, the constructor of the base class is first called, which constructs the object of the derived class. Then this constructor becomes unavailable and is extended with the code of the derived class constructor. Thus, the data of the base class is initialized first, then the data of the derived class is initialized. If classes form a hierarchy, the destructors of those classes are called in reverse order of the constructors being called. The destructor of the derived class is called first, then the destructor of the base class is called. Figure 1 shows the order of calling constructors for two classes A, B forming an inheritance hierarchy for the case of creating an instance of a derived class B. Figure 1. The order of calling constructors for the case of two classes: 1 – class A constructor; 2 – class B constructor; 3 – class B destructor; 4 – class Adestructor ⇑2. An example demonstrating the order in which constructors are called. 3 classes under considerationThe example declares 3 classes A, B, C, which form an inheritance hierarchy. Each class contains one constructor and one destructor. For the purpose of visualization, the code of constructors and destructors contains the output of the relevant information. #include <iostream> using namespace std; // Base class class A { public: // Constructor of class A A() { cout << "Constructor A::A()" << endl; } // Destructor of class A ~A() { cout << "Destructor A::~A()" << endl; } }; // Derived classes class B : public A { public: // Constructor B() { cout << "Constructor B::B()" << endl; } // Destructor ~B() { cout << "Destructor B::~B()" << endl; } }; class C :public B { public: // Constructor C() { cout << "Constructor C::C()" << endl; } // Destructor ~C() { cout << "Destructor C::~C()" << endl; } }; void main() { // Create an instance of class C C obj; // A() => B() => C() } // ~C() => ~B() => ~A() Program result Constructor A::A() Constructor B::B() Constructor C::C() Destructor C::~C() Destructor B::~B() Destructor A::~A() Based on the result obtained, the following conclusions can be drawn:
⇑3. Passing arguments to the base class when calling constructorsIf the base class has a constructor that takes parameters, then when creating an instance of a derived class, you must pass the appropriate arguments to the base class constructor. That is, you need to initialize the data of the base class in the constructor of the derived class. Such initialization is carried out using a special syntax that looks like this: // Base class class Base { // Base class constructor receiving parameters Base(params_Base) { // ... } } // Derived class class Derived : Base { // Derived class constructor - calls the base class constructor Derived(params_Derived) : Base(args_Base) { // ... } } here
If there is no constructor at all in the Base class, or there is only one parameterless constructor Base() (the default constructor), then there is no need to access the constructor of the Base class from the constructor of the derived class Derived. ⇑4. Example demonstrating passing arguments to the base class constructorThe example declares two classes:
#include <iostream> using namespace std; // A class that describes a point class Point { private: double x, y; // point coordinates public: // Constructor that receives 2 parameters Point(double _x, double _y) : x(_x), y(_y) { } // Access methods double X() { return x; } double Y() { return y; } }; // A class that inherits from the Point class, this class adds color to the Point class class PointColor : public Point { private: int color; // additional field - color public: // Constructor that takes 3 parameters, // this constructor calls the base class constructor Point(_x, _y) PointColor(double _x, double _y, int _color) : Point(_x, _y) { color = _color; } // Access method int Color() { return color; } }; void main() { // Create an instance of the PointColor class. // Constructors are called: Point(5, 8) => PointColor(2) PointColor pc(5, 8, 2); cout << "pc.x = " << pc.X() << endl; cout << "pc.y = " << pc.Y() << endl; cout << "pc.color = " << pc.Color() << endl; } In the above code, in the PointColor class, the constructor of this class calls the constructor of the base class Point, passing it two arguments (x, y coordinates). // Constructor that takes 3 parameters, // this constructor will call the base class constructor Point(_x, _y) PointColor(double _x, double _y, int _color) : Point(_x, _y) { color = _color; } The third parameter _color initializes the internal variable color. After running the program will give the following result pc.x = 5 pc.y = 8 pc.color = 2 ⇑5. Elements of a class that cannot be inheritedIn C++, not all members of a class can be inherited because they are incompatible with the very idea of inheritance. These elements include:
⇑6. Features of using a pointer to the base classIf classes form an inheritance hierarchy, then the following rule is true for these classes:
Figure 2 shows an example of a hierarchy and possible assignments to a pointer of values of the addresses of instances of derived classes Figure 2. Pointer p to base class A can point to instances of derived classes inherited from base class A ⇑7. An example demonstrating the use of a pointer to a base classThe example declares two classes Base and Derived. Classes form an inheritance hierarchy. The Derived class inherits from the Base class. // Base class class Base { // ... }; // Derived (inherited) class here the public keyword is required class Derived : public Base { // ... }; void main() { // 1. Declare a pointer to a base class Base* p1; // 2. Declare instances of base and derived class Base obj1; Derived obj2; // 3. Assign instance addresses to pointer p1 = &obj1; // It's possible p1 = &obj2; // It's possible, now p points to an instance of the derived class // 4. You can't do that Derived* p2; // Pointer to derived class p2 = &obj2; // so it is possible, the types are the same: Derived* <= Derived* p2 = &obj1; // compile error, base class cannot be extended // to derived class capabilities Derived* <= Base* } In the above example, the main() function declares a pointer to the base class Base and two instances (objects) of the Base and Derived classes, named obj1 and obj2, respectively. Then the pointer p is assigned in turn the values of the addresses of the instances obj1 and obj2. Assignment is successful. The next step is to declare a pointer p2 to the derived class Derived for demonstration purposes. This pointer alternately takes the value of the addresses of the obj2 and obj1 instances. In the case of the obj2 instance, the assignment is correct Derived* p2; p2 = &obj2; // everything works here, types are the same In case of assigning the address of the obj1 instance p2 = &obj1; the compiler throws an error. This is logical because the Base class instance obj1 cannot be extended to the capabilities of a Derived derived class because it does not inherit that class (the Base class does not inherit the Derived class). ⇑Related topics
⇑What order are destructors called?Destructors for virtual base classes are called in the reverse order of declaration.
Which destructor is called first in inheritance?This is why the constructor of base class is called first to initialize all the inherited members.
In what order destructors are executed in the inheritance hierarchy?The destructors for these virtual base classes are executed in the reverse order they appear in a depth-first left-to-right traversal of the graph of base classes, where left to right refer to the order of appearance of base class names.
In what order are class constructors and class destructors called when a derived class object is created and deleted?Destructors are called in reversed order of called constructors.
How destructors are called in inheritance?Destructors are called automatically when a variable goes out of scope. Because the base class destructor is inherited, and because the derived class object "is" a base class object, both the derived class destructor (even if it is the "default" destructor) and the base class destructor are called automatically.
|