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:
Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.
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.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.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.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.
static
s 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]
- 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]
- 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
Createprocess Doesn't Pass Command Line Arguments
Is There C/C++ Equivalent of Eval("Function(Arg1, Arg2)")
How to Use Setenv() to Export a Variable in C++
How to Deal with Global-Constructor Warning in Clang
Why the Sizeof(Bool) Is Not Defined to Be One, by the Standard Itself
2D Diamond (Isometric) Map Editor - Textures Extended Infinitely
Sizeof in C++ Showing String Size One Less
How to Typedef a Function Pointer with the C++11 Using Syntax
What Is Different Between Join() and Detach() for Multi Threading in C++
Reason for Using Non-Type Template Parameter Instead of Regular Parameter
Portable Zip Library for C/C++ (Not an Application)
Assembly Adc (Add with Carry) to C++
Detect Gcc Compile-Time Flags of a Binary
Two Phase Name Lookup for C++ Templates - Why