Writing of subclass constructor in C + + inheritance

Posted by jaql on Wed, 29 Dec 2021 14:41:12 +0100

Reproduced at: https://www.cnblogs.com/shmilxu/p/4849097.html

The constructor is used to initialize the object of the class. Unlike other members of the parent class, it cannot be inherited by the subclass (a subclass can inherit all member variables and member methods of the parent class, but does not inherit the constructor of the parent class). Therefore, when creating a subclass object, in order to initialize the data members inherited from the parent class, the system needs to call the constructor of its parent class.

If there is no explicit constructor, the compiler gives a default constructor, and the default constructor is created only without explicitly declaring the constructor.

The construction principle is as follows:

    1. If the subclass does not define a constructor, the parameterless constructor of the parent class is called.

    2. If a subclass defines a construction method, whether it is parameterless or parameterless, when creating an object of a subclass, first execute the parameterless construction method of the parent class, and then execute its own construction method.

    3. When creating a subclass object, if the constructor of the subclass does not show the constructor of calling the parent class, the default parameterless constructor of the parent class will be called.

    4. When creating a subclass object, if the constructor of the subclass does not display the constructor of the calling parent class and the parent class provides a parameterless constructor, the parameterless constructor of the parent class will be called.

    5. When creating a subclass object, if the constructor of the subclass does not display the constructor that calls the parent class and the parent class only defines its own parameterized constructor, an error will occur (if the parent class has only a parameterized constructor, the subclass must display and call this parameterized constructor).

    6. If a subclass calls the constructor with parameters of the parent class, it needs to initialize the member object of the parent class, such as:

 1 #include <iostream.h>
 2 
 3   class animal
 4   {
 5   public:
 6     animal(int height, int weight)
 7     {
 8       cout<<"animal construct"<<endl;
 9     }
10     ...
11   };
12 
13   class fish:public animal
14   {
15   public:
16     fish():animal(400,300)
17     {
18       cout<<"fish construct"<<endl;
19     }
20     ...
21   };
22   void main()
23   {
24     fish fh;
25   }

After the constructor of the fish class, add a colon (:), and then add the constructor with parameters of the parent class. In this way, when the constructor of the child class is called, the system will call the constructor with parameters of the parent class to construct the object. This initialization method is also commonly used to initialize the constant (const) members in the class, as shown in the following code:

1 class point
2   {
3   public:
4      point():x(0),y(0)
5   private:
6      const int x;
7      const int y;
8   };

Of course, ordinary member variables in a class can also be initialized in this way. However, this is not necessary.

In recent work, a software function has degraded. After tracing, it is found that the member variables of a class have not been initialized correctly. This problem is related to the writing of derived class constructor in the case of virtual inheritance in C + +. Here is the reason for the error. I hope it will be helpful to more people.

The inheritance structure of classes with virtual inheritance in our code is similar to the following figure, which is not the classic diamond structure in textbooks. Inheritance from intermediate1 and Intermediate3 to base2 is virtual. Base1 and base2 contain some member variables and provide corresponding constructors to accept the specified initialization values. Base2 also has a default constructor that initializes its member variables to 0. Intermediate1, 2 and 3 also provide a constructor to accept the specified initialization value, and call the constructors of base1 and base2 in the initialization list to complete initialization.

A colleague accidentally changed the Final code to:

 1 class Final : public Intermediate2, public Intermediate3 {
 2 public:
 3     Final (int a, int b, int c)
 4         : Intermediate2(a, b, c),
 5           Intermediate3(b, c)
 6     {
 7  
 8     }
 9  
10 };
 1 class Intermediate1 : public Base1, virtual public Base2 {
 2 public:
 3     Intermediate1(int a, int b, int c)
 4         : Base1(a), 
 5           Base2(b, c)
 6     {
 7  
 8     }
 9 };
10  
11 class Intermediate2 : public Intermediate1 {
12 public:
13     Intermediate2(int a, int b, int c)
14         : Intermediate1(a, b, c),
15           Base2(b, c)
16     {
17  
18     }
19 };
20  
21 class Intermediate3 : virtual public Base2 {
22 public:
23     Intermediate3(int b, int c)
24         : Base2(b, c)
25     {
26  
27     }
28 };

It seems that the constructor of Final will call the constructors of Intermediate2 and Intermediate3, respectively_ a, m_b and m_c is initialized to the specified value. However, the runtime found m_b and M_ The value of C is 0! Obviously, this is the default constructor that calls Base2.

Originally, the rule of C + + is that if there is a virtual inherited base class on the inheritance chain, the lowest subclass should be responsible for the construction of some members of the virtual base class. We can explicitly call the constructor of the virtual base class to complete initialization. If the constructor of the virtual base class is not explicitly called, the compiler calls the default constructor of the virtual base class. If the constructor of the virtual base class is not explicitly called, and the virtual base class does not define a default constructor, a compilation error occurs. The reason for this rule is that if this is not done, the virtual base class part will be initialized multiple times on multiple inheritance chains.

Many times, for intermediate classes on the inheritance chain, we will explicitly call the constructor of the virtual base class in its constructor, because once someone wants to create the objects of these intermediate classes, we also need to ensure that they are properly initialized.

So, if we're going to put m_b and m_c is initialized to the specified value. The correct writing of the Final constructor should be as follows:

1 Final (int a, int b, int c)
2     : Base2(b, c),
3       Intermediate2(a, b, c),
4       Intermediate3(b, c)
5 {
6  
7 }

The complete test program is shown below. Interested students can compile and run it by themselves. You can also run the Final constructor step by step in the debugger to see which constructor of Base2 is called in the first and second writing methods.

 1 #include "stdafx.h"
 2 #include <iostream>
 3  
 4 using namespace std;
 5  
 6 class Base1 {
 7 public:
 8     Base1(int a): m_a(a) {}
 9  
10 protected:
11     int m_a;
12 };
13  
14 class Base2 {
15 public:
16     Base2(int b, int c): m_b(b), m_c(c) {}
17     Base2() : m_b(0), m_c(0) {}
18  
19 protected:
20     int m_b;
21     int m_c;
22 };
23  
24 class Intermediate1 : public Base1, virtual public Base2 {
25 public:
26     Intermediate1(int a, int b, int c)
27         : Base1(a), 
28           Base2(b, c)
29     {
30  
31     }
32 };
33  
34 class Intermediate2 : public Intermediate1 {
35 public:
36     Intermediate2(int a, int b, int c)
37         : Intermediate1(a, b, c),
38           Base2(b, c)
39     {
40  
41     }
42 };
43  
44 class Intermediate3 : virtual public Base2 {
45 public:
46     Intermediate3(int b, int c)
47         : Base2(b, c)
48     {
49  
50     }
51 };
52  
53 class Final : public Intermediate2, public Intermediate3 {
54 public:
55     Final (int a, int b, int c)
56         : Base2(b, c),
57           Intermediate2(a, b, c),
58           Intermediate3(b, c)
59     {
60  
61     }
62  
63     void Print() {
64         cout<<m_a<<", "<<m_b<<", "<<m_c<<endl;
65     }
66 };
67  
68  
69 int _tmain(int argc, _TCHAR* argv[])
70 {
71     Final finalObj(1, 2, 3);
72     finalObj.Print();
73  
74     return 0;
75 }

Topics: C++