Member Fields, Order of Construction

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.

Is the order of construction of member variables of a class undefined in c++?

No. Members are constructed in the order in which they are declared.

You are advised to arrange your initializer list in the same order, but you are not required to do so. It's just very confusing if you don't and may lead to hard-to-detect errors.

Example:

struct Foo {
int a; int b;
Foo() : b(4), a(b) { } // does not do what you think!
};

This construction is actually undefined behaviour, because you're reading an uninitialized variable in the initializer a(b).


Standard reference (C++11, 12.6.2/10):

— 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).

Order of items in classes: Fields, Properties, Constructors, Methods

According to the StyleCop Rules Documentation the ordering is as follows.

Within a class, struct or interface: (SA1201 and SA1203)

  • Constant Fields
  • Fields
  • Constructors
  • Finalizers (Destructors)
  • Delegates
  • Events
  • Enums
  • Interfaces (interface implementations)
  • Properties
  • Indexers
  • Methods
  • Structs
  • Classes

Within each of these groups order by access: (SA1202)

  • public
  • internal
  • protected internal
  • protected
  • private

Within each of the access groups, order by static, then non-static: (SA1204)

  • static
  • non-static

Within each of the static/non-static groups of fields, order by readonly, then non-readonly : (SA1214 and SA1215)

  • readonly
  • non-readonly

An unrolled list is 130 lines long, so I won't unroll it here. The methods part unrolled is:

  • public static methods
  • public methods
  • internal static methods
  • internal methods
  • protected internal static methods
  • protected internal methods
  • protected static methods
  • protected methods
  • private static methods
  • private methods

The documentation notes that if the prescribed order isn't suitable - say, multiple interfaces are being implemented, and the interface methods and properties should be grouped together - then use a partial class to group the related methods and properties together.

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

Order of construction/initialization?

I was under the impression any nonstatic class fields are initialized before the constructor runs.

Your impression is / was incorrect.

What actually happens is that all superclass initialization for an object happens before any subclass initialization for the object.

The above rule-of-thumb is for object instance initialization only. Class (i.e. static) initialization is a bit more complicated. But this question is about instance initialization.

The process is described in detail in JLS 12.5. The relevant part is this:

Just before a reference to the newly created object is returned as the
result, the indicated constructor is processed to initialize the new
object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then
    evaluate the arguments and process that constructor invocation
    recursively using these same five steps. If that constructor
    invocation completes abruptly, then this procedure completes abruptly
    for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If
    this constructor is for a class other than Object, then this
    constructor will begin with an explicit or implicit invocation of a
    superclass constructor (using super). Evaluate the arguments and
    process that superclass constructor invocation recursively using these
    same five steps.
    If that constructor invocation completes abruptly,
    then this procedure completes abruptly for the same reason. Otherwise,
    continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable
    initializers to the corresponding instance variables, in the
    left-to-right order in which they appear textually in the source code
    for the class. If execution of any of these initializers results in an
    exception, then no further initializers are processed and this
    procedure completes abruptly with that same exception. Otherwise,
    continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly
    for the same reason. Otherwise, this procedure completes normally.

Note the sentence that I have highlight. When you take that in the context of the rest, that means that the constructor body for Base() runs before the initialization of the instance fields for Outer.


Other JVMs might or might not behave the same way.

In fact all JVMs should behave this way. This is how the Java language is specified to behave.

Order of member constructor and destructor calls

In other words, are members guaranteed to be initialized by order of declaration and destroyed in reverse order?

Yes to both. See 12.6.2

6 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, non-static 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
    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 ]

Change the order of constructors in inheritance

Your constant B::CONSTANT_B can be static, since it does not depend on a constructor argument.

statics are initialised before your class objects are constructed (unless those are static too!).

struct B : A
{
static const int CONSTANT_B = 3;

B() : A(CONSTANT_B)
{
write();
}
};

If B::CONSTANT_B itself took its value from a constructor argument, you'd probably have to name that argument twice in the ctor-member-initialiser. There are no trivial workarounds for that as far as I can imagine.

struct B : A
{
const int CONSTANT_B;

B(const int constant)
: A(constant)
, CONSTANT_B(constant)
{
write();
}
};

construction order of static member in class

The behavior your seeing is proper because both main() and B::a reside in the same translation unit. From the standard, leaving out the thread-local initialization stuff, you're seeing dynamic initialization of an object with static storage duration. The standard does have rules of how/when objects with static storage duration are initialized:

C++ § 3.6.2 [basic.start.init]


  1. Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place.

There's more dealing with constexpr declarations, but that doesn't apply to your code. So we at least know the memory backdrop of object B::a has been zero-initialized, goodie, but when does the constructor actually fire in this case? That is called dynamic initialization, and according to the standard:

C++ § 3.6.2 [basic.start.init]


  1. It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

In your case, as circumstances would have it main() is in the same translation unit as B::a, In staying with the standard, while main() may start execution before static-storage objects are initialized, main() itself resides in a translation unit, and as such all static-storage objects requiring dynamic initialization in the same unit as main() must do so before main() begins execution. Therefore B::a is constructed before the first statement of main().

Why should you care? Well, B::a could always reside in s different translation unit than main() (ex: b.cpp), and your results may be different, but still in compliance with the standard. At that point it is implementation-defined whether B::a would be dynamically initialized before main() begins execution, but again, must be initialized before any function or variable in the b.cpp translation unit is odr-used.



Related Topics



Leave a reply



Submit