initialization list 20201203

definition
Unlike other functions, constructors can have initialization lists in addition to names, parameter lists, and function bodies. The initialization list begins with a colon, followed by a series of comma-separated initialization fields.

class foo
{
public:
foo(string s, int i):name(s), id(i){} ; // initialization list
private:
string name ;int id ;
};

Conceptually, the execution of the constructor can be divided into two phases, the initialization phase and the calculation phase, the initialization phase precedes the calculation phase.

initialization phase
All members of a class type are initialized during initialization, even if the member does not appear in the constructor's initialization list.
calculation phase
Generally used to perform assignment operations in the body of the constructor.

     In the following code, Test1 has a constructor, a copy constructor and an assignment operator to facilitate viewing the results. Test2 is a test class that uses the object of Test1 as a member. Let's see how the constructor of Test2 is executed. of.

class Test1
{
public:
Test1() // no-argument constructor
{
	cout << "Construct Test1" << endl ;
}
Test1(const Test1& t1) // copy constructor
{
	cout << "Copy constructor for Test1" << endl ;this->a = t1.a ;
}
Test1& operator = (const Test1& t1) // assignment operator
{
	cout << "assignment for Test1" << endl ;
	this->a = t1.a ;
	return *this;
}
int a ;
};
class Test2
{
public:
Test1 test1 ;
Test2(Test1 &t1)
{
	test1 = t1 ;
}
};

calling code:

Test1 t1 ;
Test2 t2(t1) ;

output:

explain:
The first line of output corresponds to the first line in the calling code, constructing a Test1 object
The second line of output corresponds to the code in the Test2 constructor, initializing the object test1 with the default constructor // This is the so-called initialization phase
The third line outputs the assignment operator corresponding to Test2, and performs the assignment operation on test1 // This is the so-called calculation phase

Reasons to use an initializer list
There are two ways to initialize members of a class:
1. Use an initialization list.
2. The assignment operation is performed in the body of the constructor.

Mainly a performance issue. For built-in types, such as int, float, etc., the difference between initializing the class table and initializing in the constructor body is not very different, but for class types, it is better to use the initialization list. Why?

As can be seen from the following test, using the initialization list reduces the process of calling the default constructor once, which is very efficient for data-intensive classes. Also looking at the above example, we use the initialization list to implement the constructor of Test2.

class Test2
{
public:
Test1 test1 ;
Test2(Test1 &t1):test1(t1){}
}

Using the same calling code, the output is as follows:

The first line of output corresponds to the first line of the calling code
The second line outputs the initialization list corresponding to Test2, and directly calls the copy constructor to initialize test1, omitting the process of calling the default constructor.
So a good rule of thumb is to use initializer lists whenever possible.

When you must use an initializer list
In addition to performance issues, there are times when an initializer list is indispensable, and the initializer list must be used in the following cases
1. Constant members, because constants can only be initialized and cannot be assigned, so they must be placed in the initialization list
2. Reference type, the reference must be initialized at the time of definition, and cannot be reassigned, so it should also be written in the initialization list
3. A class type without a default constructor, because using the initialization list does not need to call the default constructor to initialize, but directly calls the copy constructor to initialize

class Test1
{
public:
Test1(int a):i(a)
{
}
int i;
};
class Test2
{
public:
Test1 test1 ;
Test2(Test1 &t1)
{
	test1 = t1 ;
}
};

The above code cannot be compiled because the line test1 = t1 in the constructor of Test2 is actually executed in two steps:

  1. Call Test1's default constructor to initialize test1
    Since Test1 does not have a default constructor, 1 cannot be executed, resulting in a compilation error. The correct code is as follows, using initialization list instead of assignment operation
class Test2
{
public:
Test1 test1 ;
Test2(int x):test1(x){}
}
  1. Call the assignment function.

order of member variables
Members are initialized in the order they appear in the class, not in the order they appear in the initialization list, see the code:

class foo
{
public:
int i ;int j ;
foo(int x):i(x), j(i){}; // ok, initialize i first, then initialize j
};

Look at the following code again:

class foo
{
public:
int i ;int j ;
foo(int x):j(x), i(j){} // i value is undefined
};

The value of i here is undefined because although j appears before i in the initialization list, i is defined before j, so i is initialized first, and i is initialized by j. At this time, j has not been initialized, so the value of i is not definition. A good habit is to initialize members in the order they are defined.

from this article Link

Posted by bladechob on Wed, 04 May 2022 01:38:08 +0300