C++: Construction and Initialization Order Guarantees

C++: Construction and initialization order guarantees

In all classes construction order is guaranteed: base classes, as specified from left to right followed by member variables in the order declared in the class definition. A class's constructor body is executed once all of its bases' and members' constructions have completed.

In your example X is derived from Z and contains Y so the Z base object is constructed first, then the Y member y, then the construction of the X completes with the execution of X's constructor body.

The temporary W is needed to pass to the constructor of X, so it is constructed before the construction of the x begins and will be destroyed once the initialization of x completes.

So the program must print:

W
Z
Y
X

Member fields, order of construction

Yes, the order of construction is always guaranteed. It is not, however, guaranteed to be the same as the order in which the objects appear in the initializer list.

Member variables are constructed in the order in which they are declared in the body of the class. For example:

struct A { };
struct B { };

struct S {
A a;
B b;

S() : b(), a() { }
};

a is constructed first, then b. The order in which member variables appear in the initializer list is irrelevant.

Initialization order guarantees for inline-initialized static const class member

For static initialization, until C++14, zero initialization happens before constant initialization. Since C++14, constant initialization happens instead of zero initialization. Constant initialization basically happens for objects and references that are value-initialized by constant expressions (or constexpr constructors). Details here.

The compiler is permitted to initialize other static objects using constant initialization, if it can guarantee that the value would be the same as if the standard order of initialization was followed. In practice, constant initialization is performed at compile time and pre-calculated object representations are stored as part of the program image. If the compiler doesn't do that, it still has to guarantee that this initialization happens before any dynamic initialization.

m_myMember is not static initialized, because its class constructor is not constexpr. g_myConstant is constant initialized. Using static constexpr forces the compiler to initialize the value at compiler time, without constexpr, the compiler may initialize at compile time. Your construction is well-defined.

Look this example from cppreference:

#include <iostream>
#include <array>

struct S {
static const int c;
};
const int d = 10 * S::c; // not a constant expression: S::c has no preceding
// initializer, this initialization happens after const
const int S::c = 5; // constant initialization, guaranteed to happen first
int main()
{
std::cout << "d = " << d << '\n';
std::array<int, S::c> a1; // OK: S::c is a constant expression
// std::array<int, d> a2; // error: d is not a constant expression
}

But now, if we change init order, it compiles:

#include <iostream>
#include <array>

struct S {
static const int c;
};
//both inits are now constant initialization
const int S::c = 5;
const int d = 10 * S::c;
int main()
{
std::cout << "d = " << d << '\n';
std::array<int, S::c> a1;
std::array<int, d> a2; //now, it's ok
}

Order of initialization

An example would be cout.

The compiler doesn't really guarantee that those objects are built before they are used. Take this code:

struct Foo
{

Foo();
}foo;

#include <iostream>

Foo::Foo()
{
std::cout << "Hello World";
}

int main() {}

It segfaults because the foo object gets built before cout. When it tries to use cout, bad things happen.

Basically, all the global objects will get code inserted to construct them which runs before main. Once you are in main you are safe, all objects are built. Before that it depends on the order in which they are defined. That's why I can break it by including iostream after declaring the foo global.

Constructor initialization-list evaluation order

It depends on the order of member variable declaration in the class. So a_ will be the first one, then b_ will be the second one in your example.

Class component order of initialisation

From the C++03 standard ISO/IEC 14882:2003(E) §12.6.2/5 [class.base.init]:

Initialization shall proceed in the following order:

— First, and only for the constructor of the most derived class as described below, virtual base classes shall be 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 class names in the derived class base-specifier-list.

— Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

— Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

— Finally, the body of the constructor is executed.

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

So in this case, you are guaranteed that the order of initialization will be first the base class A, then the subobject B (since it appears first in the list of class members in the class definition), then the subobject C. The order of the initializer list is irrelevant, as is whether or not any of the members do or do not have a default constructor—if a member does not have a default constructor and it is not explicitly initialized in an initializer list, then it has an unspecified value.

Ensure the construction and destruction order of static variables in c++

In your case you are using construct on first use, and none of the constructors of your classes dependent on the other. Thus the order of initialization is guaranteed A then B.

The destruction order in this case it is guaranteed to be as such B->A, as long as you have simple destructors. Here is a more elaborate answer.

Initialization order: An array with a separate pointer to that same array

is this legal

Yes

is this undefined behavior

No

would the compiler be allowed by the C++ standard to reorder these declarations so that ptr is initialized to some junk value?

No, it initializes ptr with the char* that buf decays into. If a compiler would be allowed to initialize them in the other order, it would need to fail compiling, since it wouldn't have seen buf yet.

Compare with these:

OK:

char buf[7];
char *ptr = buf;

Error:

char *ptr = buf;
char buf[7];

What is the construction order in a diamond problem where a class is derived from three base classes?

Construction order is always this:

  1. First all (direct or indirect) virtual bases, ordered depth-first declaration-order. Obviously at most one of any type.
  2. Next, the non-virtual bases in declaration order.
  3. All other members in declaration order.
  4. Finally, the ctor body. Now you may safely call virtual functions, directly or indirectly, and they will resolve based on the currently running ctor.

In your case that means:

  1. virtual X from Y::A::X.
  2. virtual B from Y::B:

    (First step: X from Y::B::X.)
  3. A from Y::A. X virtual base already done.
  4. C from Y::C. X virtual base already done.

Destruction order reverses that.



Related Topics



Leave a reply



Submit