对象的构造和析构顺序

Through a CppQuiz question to describe the order of construction and destruction of C++ objects in the context of inheritance according to the C++14 standard, as well as throwing exceptions during object construction/destruction.

First, let’s outline the standard:

Order of Execution During Object Construction

In a non-delegating constructor, initialization proceeds in the following order:

  • First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
  • Finally, the compound-statement of the constructor body is executed.

[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. - end note ]

Order of Execution During Object Destruction

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see 12.6.2). A return statement (6.6.3) in a destructor might not directly return to the caller; before transferring control to the caller, the destructors for the members and bases are called. Destructors for elements of an array are called in reverse order of their construction (see 12.6).

Throwing Exceptions During Construction and Destruction

An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <exception>

int x = 0;

class A {
public:
A() {
std::cout << 'a';
if (x++ == 0) {
throw std::exception();
}
}
~A() { std::cout << 'A'; }
};

class B {
public:
B() { std::cout << 'b'; }
~B() { std::cout << 'B'; }
A a;
};

void foo() { static B b; }

int main() {
try {
foo();
}
catch (std::exception &) {
std::cout << 'c';
foo();
}
}

Answer: acabBA

  1. First, calling foo(), the foo function body creates a static B object. Since no initializer is provided, B’s default constructor is called.
  2. According to the order of execution during object construction, base classes (if any) and data members are initialized. Because class B has no base classes and only one member of class A, as per the above rule, the initialization of member a will be executed first. Since no initializer is provided, A’s default constructor is also called.
  3. A’s default constructor outputs a, then checks that at this moment x++ == 0 is true, so an exception is thrown in A’s constructor. When an exception is thrown in a constructor, destructors are executed for all its fully constructed subobjects (excluding variant members of a union-like class), so, since there are no data members in A, A’s construction ends directly without executing any destructors.
  4. The exception is caught in catch, and a c is output.
  5. foo() is executed again, and the constructors for B and A are called sequentially. First, the constructor for the data member a in B is called (outputting a). Since x++ has been executed before, now x++ == 0 is false, so no exception is thrown, and the A object is constructed successfully.
  6. The constructor body of B is executed, outputting b.
  7. When the program ends, the B class object b is destructed, following the order of execution during object destruction (in reverse order to construction).
  8. According to the above rules, the destructor for b is called, outputting B.
  9. Then, the destructor for class A’s data member a is called, outputting A.
The article is finished. If you have any questions, please comment and communicate.

Scan the QR code on WeChat and follow me.

Title:对象的构造和析构顺序
Author:LIPENGZHA
Publish Date:2017/02/19 18:04
World Count:3.6k Words
Link:https://en.imzlp.com/posts/16550/
License: CC BY-NC-SA 4.0
Reprinting of the full article is prohibited.
Your donation will encourage me to keep creating!