Initializer List

Initializer List用於初始化Class的Data member。 Constructor(構造函數)將要初始化的member list放在冒號後面(以逗號分隔)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<iostream> 
using namespace std;

class Point {
private:
int x;
int y;
public:
Point(int i = 0, int j = 0):x(i), y(j) {}
/* The above use of Initializer list is optional as the
constructor can also be written as:
Point(int i = 0, int j = 0) {
x = i;
y = j;
}
*/

int getX() const {return x;}
int getY() const {return y;}
};

int main() {
Point t1(10, 15);
cout<<"x = "<<t1.getX()<<", ";
cout<<"y = "<<t1.getY();
return 0;
}

/* OUTPUT:
x = 10, y = 15
*/

什麼時侯要用 Initializer List?

在某些情況下,我們無法直接初始化Constructor(構造函數)中的Data member,而必須使用“Initializer List(初始化列表)”。

當你要初始化 non-static const data member

const Data member 必須使用Initializer List進行初始化。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream> 
using namespace std;

class Test {
const int t;
public:
Test(int t):t(t) {} //Initializer list must be used
int getT() { return t; }
};

int main() {
Test t1(10);
cout<<t1.getT();
return 0;
}

/* OUTPUT:
10
*/

t是Test類的const data member,並使用Initializer List進行初始化。

Reason for initializing the const data member in initializer list is because no memory is allocated separately for const data member, it is folded in the symbol table due to which we need to initialize it in the initializer list.

另外,它是一個copy constructor(複制構造函數),我們不需要調用賦值運算符(±*/那些),這意味著我們避免了一個額外的操作。

當你要初始化 reference member

當你要初始化帶有&的Data member,你都要用到Initializer List。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Initialization of reference data members 
#include<iostream>
using namespace std;

class Test {
int &t;
public:
Test(int &t):t(t) {} //Initializer list must be used
int getT() { return t; }
};

int main() {
int x = 20;
Test t1(x);
cout<<t1.getT()<<endl;
x = 30;
cout<<t1.getT()<<endl;
return 0;
}
/* OUTPUT:
20
30
*/

當你要初始化沒有constructor的 member object

在以下示例中,Class A 的 Object a 是 Class B的 data member,而 A 沒有默認的Constructor。

這時侯必須用Initializer List來初始化 a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream> 
using namespace std;

class A {
int i;
public:
A(int );
};

A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}

// Class B contains object of A
class B {
A a;
public:
B(int );
};

B::B(int x):a(x) { //Initializer list must be used
cout << "B's Constructor called";
}

int main() {
B obj(10);
return 0;
}
/* OUTPUT:
A's Constructor called: Value of i: 10
B's Constructor called
*/

假設現在Class A同時具有默認Constructor和參數化Constructor。
如果要使用默認Construct初始化a,則不必使用Initializer List,而必須使用參數化(Parameterized)Constructor初始化a

當你要初始化 base class members

Base class 的 Parameterized Constructor 只能使用nitializer List進行呼叫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream> 
using namespace std;

class A {
int i;
public:
A(int );
};

A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}

// Class B is derived from A
class B: A {
public:
B(int );
};

B::B(int x):A(x) { //Initializer list must be used
cout << "B's Constructor called";
}

int main() {
B obj(10);
return 0;
}

當Constructor的參數名稱與Data member相同時

如果Constructor的參數名稱與Data member名稱相同,則必須使用此Pointer(指針)或Initializer List來初始化Data member。 在以下示例中,A()的member名稱和parameter名稱均為i

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream> 
using namespace std;

class A {
int i;
public:
A(int );
int getI() const { return i; }
};

A::A(int i):i(i) { } // Either Initializer list or this pointer must be used
/* The above constructor can also be written as
A::A(int i) {
this->i = i;
}
*/

int main() {
A a(10);
cout<<a.getI();
return 0;
}
/* OUTPUT:
10
*/

從Performance角度

最好在Initializer List中初始化所有Class variable,而不是在body內部assign value。

看看以下示例:

1
2
3
4
5
6
7
8
9
10
11
// Without Initializer List 
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};

Here compiler follows following steps to create an object of type MyClass

  1. Type’s constructor is called first for a.
  2. The assignment operator of Type is called inside body of MyClass() constructor to assign
1
variable = a; 
  1. And then finally destructor of Type is called for a since it goes out of scope.

Now consider the same code with MyClass() constructor with Initializer List

1
2
3
4
5
6
7
8
9
10
// With Initializer List 
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};

With the Initializer List, the following steps are followed by compiler:

  1. Copy constructor of Type class is called to initialize: variable(a). The arguments in the initializer list are used to copy construct variable directly.
  2. The destructor of Type is called for a since it goes out of scope.

As we can see from this example if we use assignment inside constructor body there are three function calls: constructor + destructor + one addition assignment operator call. And if we use Initializer List there are only two function calls: copy constructor + destructor call. See this post for a running example on this point.

Reference

What does the colon after a constructor mean?
Variables after the colon in a constructor
Understanding Initialization Lists in C++
建構函式 (C++)
When do we use Initializer List in C++?