Will the initialization list always be processed before the constructor code?
In other words, will the following code always print <unknown>
, and the constructed class will have "known" as value for source_
(if the global variable something
is true
)?
class Foo {
std::string source_;
public:
Foo() : source_("<unknown>") {
std::cout << source_ << std::endl;
if(something){
source_ = "known";
}
}
};
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.
The order of the initializer list does NOT matter. The declaration of your members in the class header defines the initialization order. This is by design and required as you could have multiple ctors having totally different init list orders.
Initialization lists allow you to choose which constructor is called and what arguments that constructor receives. If you have a reference or a const field, or if one of the classes used does not have a default constructor, you must use an initialization list.
The constructors should be used to initialize member variables of the class because member variables cannot be declared or defined in a single statement. Therefore, constructors are used in initializing data members of a class when an object is created. Below is the C++ program to illustrate the above concept: C++
The constructor initializer list is below: In the above example, one may think the initialization order is m_glass, m_tyres and m_engine. But the order is Engine, Tyres, and Glass.
If class A had both default and parameterized constructors, then Initializer List is not must if we want to initialize “a” using default constructor, but it is must to initialize “a” using parameterized constructor.
The constructor initializer list is below: In the above example, one may think the initialization order is m_glass, m_tyres and m_engine. But the order is Engine, Tyres, and Glass. This is because C++ respects the Objects layout in the class template and not the order in the Constructor.
The initialization list is written after the name of the constructor starting with the colon followed by the data members that need to be initialized. The initialization list initializes the members without pre initializing them with empty or garbage values (which happens in normal initialization done inside the constructor).
Yes, it will, as per C++11: 12.6.2 /10
(same section in C++14
, 15.6.2 /13
in C++17
):
In a non-delegating constructor, initialization proceeds in the following order (my bold):
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.
The main reason for using init-lists is to help the compiler with optimisation. Init-lists for non-basic types (i.e., class objects rather than int
, float
, etc.) can generally be constructed in-place.
If you create the object then assign to it in the constructor, this generally results in the creation and destruction of temporary objects, which is inefficient.
Init-lists can avoid this (if the compiler is up to it, of course but most of them should be).
The following complete program will output 7 but this is for a specific compiler (CygWin g++) so it doesn't guarantee that behaviour any more than the sample in the original question.
However, as per the citation in the first paragraph above, the standard does actually guarantee it.
#include <iostream>
class Foo {
int x;
public:
Foo(): x(7) {
std::cout << x << std::endl;
}
};
int main (void) {
Foo foo;
return 0;
}
Yes, C++ constructs all the members before calling the constructur code.
As already answered, initialization lists get completely executed before entering the constructor block. So it is completely safe to use (initialized) members in the constructor body.
You have made a comment in the accepted answer about having to refer to the constructor arguments, but not the member vars inside the constructor block. You don't.
It is possible that you mistook the fact that you should refer to parameters and not to member attributes inside the initialization list. As an example, given a class X that has two members (a_ and b_) of type int, the following constructor may be ill-defined:
X::X( int a ) : a_( a ), b( a_*2 ) {}
The possible problem here is that the construction of the elements in the initialization list depends on the order of declaration in the class and not the order in which you type the initialization list. If the class were defined as:
class X
{
public:
X( int a );
private:
int b_;
int a_;
};
Then, regardless of how you type the initialization list in, the fact is that b_( a_*2 ) will be executed before a_ is initialized since the declaration of the members is first b_ and later a_. That will create a bug as your code believes (and probably depends) on b_ being twice the value of a_, and in fact b_ contains garbage. The simplest solution is not refering to the members:
X::X( int a ) : a_( a ), b( a*2 ) {} // correct regardless of how X is declared
Avoiding this pitfall is the reason why you are suggested not to use member attributes as part of the initialization of other members.
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