Let's say I have the following:
class A {
B member1;
C member2;
public:
A();
};
class B {
public:
C& ref_to_c;
B( C& ref_to_c );
};
class C {
...
};
B requires that a reference to C be provided on its constructor. If class A provides C, is it legal to specify A's initialiser list as the following...
A() : member1( B( member2 ) ) {}
That is to say, does member2 exist in the initialiser list phase, or is this undefined behaviour?
Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.
Member initializer list is the place where non-default initialization of these objects can be specified. For bases and non-static data members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified.
There are two ways to initialize a class object: Using a parenthesized expression list. The compiler calls the constructor of the class using this list as the constructor's argument list. Using a single initialization value and the = operator.
To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.
Intialization is as follows:
5 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. ]
Which basically means that member1
will always be initialized before member2
. So, B
's constructor will run first.
Even if you'd call them in reverse order explictly in A
's constructor:
A() : member2(foo), member1(bar) {}
it doesn't make a difference. Now, referencing an uninitialized object is not UB by itself but it can be depending on B
's constructor. You should switch the order of declaration:
C member2;
B member1;
You are making up member1
that contains a reference to memebr2
.
That is not constructed yet, but the compiler already knows where it will be (so it can provide a reference).
It will work, but will be UB if you try -for example in B ctor- to access the ref_to_c
value in some kind of expression, since the reference is actually aliasing an uninitialized memory, that will be initialized during member2 construction, that would happen later.
The same problem will be in B destructor, where member2
will be destroyed before ref_to_c
.
It will be better if you swap member2 and member1 in A, so that you will initialize the reference with a constructed object, making every possible usage, defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With