Constructor and destructor calling order in C++ inheritance

First, why use a virtual base class?
The simple point is to save space and avoid constructing the same object multiple times under multiple inheritance conditions.

// 直接使用多继承
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : public A, public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : public A, public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public C, public D, public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}
// 结果如下, e的生命周期
A的构造函数调用
B的构造函数调用
C的构造函数调用
A的构造函数调用
B的构造函数调用
D的构造函数调用
A的构造函数调用
E的构造函数调用
E的析构函数调用
A的析构函数调用
D的析构函数调用
B的析构函数调用
A的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用


// 使用虚拟基类后
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : virtual public A, virtual public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : virtual public A, virtual public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}

// 结果,e的生命周期
A的构造函数调用
B的构造函数调用
C的构造函数调用
D的构造函数调用
E的构造函数调用
E的析构函数调用
D的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用

From this, it can be clearly seen that the construction times of the two base classes of A and B are reduced.

Next, let’s talk about their construction order, and first post the instructions on the C++ document.
insert image description here
Simply put,

  1. First initialize the virtual base pointer of the base class or object
  2. Initialize member objects
  3. call its own constructor

code:

class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : public A, public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
    A a;
    B b;
};

class D : public A, public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
    B b;
    A a;
};

class E : public C, public D, public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}

// 结果,只看对象e的构造函数的顺序,析构的没放上来。
// E需要C、D、B三个基类的构造
A的构造函数调用
B的构造函数调用	// E的多继承中C的多继承基类的构造
A的构造函数调用
B的构造函数调用	// C中的A、B两个成员对象的构造
C的构造函数调用	// C本身构造函数调用
A的构造函数调用
B的构造函数调用	// E的多继承中D的多继承基类的构造
B的构造函数调用	
A的构造函数调用	// D中的B、A两个成员对象的构造
D的构造函数调用	// D本身构造函数调用
B的构造函数调用	// E的多继承中B的构造
E的构造函数调用	// E本身构造函数的调用

// 使用虚拟基类
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : virtual public A, virtual public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
    A a;
    B b;
};

class D : virtual public A, virtual public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
    B b;
    A a;
};

class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}

// 结果
A的构造函数调用
B的构造函数调用	// C中的A、B两个成员对象的构造
A的构造函数调用
B的构造函数调用
C的构造函数调用	// C中的多继承基类的构造
B的构造函数调用
A的构造函数调用	// D中的B、A两个成员对象的构造
D的构造函数调用	// 虚拟基指针初始化指向前面构造过的A、B,无需重新构造
E的构造函数调用	// 最后虚拟基B类也是指向前面创建好的B,最后调用E的构造函数即可

The order of the destructor is described in the official document first.
insert image description here
It is explained in two cases:

  1. Non-virtual base class, simply put: objects are destructed in the reverse order of construction
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : public A, public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
    A a;
    B b;
};

class D : public A, public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
    B b;
    A a;
};

class E : public C, public D, public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}

// 结果
A的构造函数调用
B的构造函数调用
A的构造函数调用
B的构造函数调用
C的构造函数调用
A的构造函数调用
B的构造函数调用
B的构造函数调用
A的构造函数调用
D的构造函数调用
B的构造函数调用
E的构造函数调用
// 析构顺序,同构造顺序相反,先析构本身,然后析构成员对象,最后析构基类
E的析构函数调用
B的析构函数调用
D的析构函数调用
A的析构函数调用
B的析构函数调用
B的析构函数调用
A的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用
B的析构函数调用
A的析构函数调用
  1. The virtual base class, the picture of the official document,
    insert image description here
    insert image description here
    look at the code first:
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : virtual public A, virtual public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : virtual public A, virtual public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}

// 结果
E的析构函数调用
D的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用

General process:

  1. First of all, the left in the left refers to the first base class of the multiple inheritance declaration. The first base class of E is C, and the first base class of C is A. A does not inherit any class. This is the order: E > C > A
  2. Remember the last A, visit the previous C, first judge A in steps 4 and 5, whether A is in the list, if not, add to the bottom, and do nothing if it is (list: A)
  3. At this time, operate the access point C, find the next inherited class, find B, and B is the virtual base class. You don’t need to go back to the second step, and directly perform the judgment of steps 4 and 5. If it is not, add it to the bottom of the list (list: A > b)
  4. Continue to make access point C. At this time, C has no base class for class operations. Visit the previous node A of C. Remember that C, C is not a virtual base class, and it is directly added to the bottom of the list without making a judgment. (list: A > B > C)
  5. Then operate the access point A, the same final list as above is: A > B > C > D > E, and then destruct in the order of the list.

Let's use the code to give a few control experiments to understand

// 原实验
class A {
public:
    A() { cout << "A的构造函数调用" << endl; }
    ~A() { cout << "A的析构函数调用" << endl; }
};

class B {
public:
    B() { cout << "B的构造函数调用" << endl; }
    ~B() { cout << "B的析构函数调用" << endl; }
};

class C : virtual public A, virtual public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : virtual public A, virtual public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

int main()
{
    E e;
}
// 结果
E的析构函数调用
D的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用

// 下面的代码只展示不同部分,太多重复的看得不爽
// 实验1:C不使用虚拟继承
class C : public A, public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

// 结果
E的析构函数调用
D的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用 // C此时对B、A进行了析构,未使用虚拟基类会初始化,因此肯定要单独析构创建的对象,但顺序还是正确的。
B的析构函数调用
A的析构函数调用

// 实验2:改变E的C、D的生命顺序
class E : public D, public C, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

// 结果
E的析构函数调用
C的析构函数调用
D的析构函数调用	// 此时可以看到C、D的顺序相反,印证了算法是没错的
B的析构函数调用	// 但A、B顺序是没变的,因为我们没改变C、D的继承的声明顺序
A的析构函数调用

// 实验3:改变D的声明顺序
class D : virtual public B, virtual public A {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};
// E没改
class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};

// 结果,没有变化,因为B、A的顺序在C时就确定了,D的声明顺序不管紧要
E的析构函数调用
D的析构函数调用
C的析构函数调用
B的析构函数调用
A的析构函数调用

// 实验4:修改C的A、B的生命顺序
class C : virtual public B, virtual public A {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : virtual public A, virtual public B {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public C, public D, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};
// 结果
E的析构函数调用
D的析构函数调用
C的析构函数调用
A的析构函数调用
B的析构函数调用	// A、B的顺序相反了,因为他们的析构调用是根据C的声明顺序来的,改变C会改变A、B。

// 实验5:修改E的C、D的声明顺序,并改变D的声明顺序
class C : virtual public A, virtual public B {
public:
    C() { cout << "C的构造函数调用" << endl; }
    ~C() { cout << "C的析构函数调用" << endl; }
};

class D : virtual public B, virtual public A {
public:
    D() { cout << "D的构造函数调用" << endl; }
    ~D() { cout << "D的析构函数调用" << endl; }
};

class E : public D, public C, virtual public B {
public:
    E() { cout << "E的构造函数调用" << endl; }
    ~E() { cout << "E的析构函数调用" << endl; }
};
// 结果
E的析构函数调用
C的析构函数调用
D的析构函数调用
A的析构函数调用
B的析构函数调用	// 调用顺序也改变了

The above is my understanding.

Official documentation link for constructors: https://docs.microsoft.com/zh-cn/cpp/cpp/constructors-cpp?view=msvc-170
Official documentation link for destructors: https://docs.microsoft.com/ en-cn/cpp/cpp/destructors-cpp?view=msvc-170

Guess you like

Origin blog.csdn.net/A_easy_learner/article/details/125386907