在C++中,初始化列表和列表初始化是两个不同的概念,尽管它们都涉及到对象的初始化。
初始化列表(Initializer List)
初始化列表用于在构造函数中初始化类成员变量。它的语法是在构造函数的参数列表后面加一个冒号,然后列出成员变量的初始化方式。例如:
class MyClass {
public:
MyClass(int a, int b) : x(a), y(b) {}
private:
int x;
int y;
};
使用初始化列表有几个优点:
1. 提高效率
初始化列表直接调用构造函数来初始化成员变量,而不是先调用默认构造函数,再在构造函数体内赋值。对于某些类类型的数据(如对象、容器),这种方式可以避免额外的赋值操作,提高性能。上面的例子中,a
和 b
在构造函数中直接通过初始化列表被初始化,避免了默认初始化后的再赋值操作。
当你在构造函数体内进行成员变量赋值时,实际上分为两个步骤:
- 成员变量的默认构造:在进入构造函数体之前,C++会自动调用成员变量的默认构造函数来初始化这些成员变量。
- 在构造函数内赋值:进入构造函数体后,显式地进行赋值操作(比如调用赋值运算符),这会再次对这些成员变量进行赋值操作。
这意味着,对于一个非基础类型的成员变量,默认构造和赋值操作是分开的,因此会导致:
- 先调用一次默认构造函数(在进入构造函数之前)。
- 再调用赋值运算符进行赋值(在构造函数体内)。
2. 初始化常量成员
类中的 const
成员变量必须通过初始化列表初始化,因为常量成员变量一旦初始化后便不能被修改。
class MyClass {
public:
MyClass(int val) : constantVal(val) {}
private:
const int constantVal;
};
3. 初始化引用类型成员
引用类型成员变量必须通过初始化列表进行初始化,因为引用必须在其定义时就被绑定到某个对象上,无法通过赋值的方式修改。
class MyClass {
public:
MyClass(int& ref) : myRef(ref) {}
private:
int& myRef;
};
4. 初始化需要特定构造函数的成员
如果类成员对象没有默认构造函数(即没有无参构造函数),则必须使用初始化列表来调用带参数的构造函数。
class AnotherClass {
public:
AnotherClass(int) {} // 只有带参数的构造函数
};
class MyClass {
public:
MyClass(int val) : obj(val) {} // 必须使用初始化列表
private:
AnotherClass obj;
};
总结:初始化列表必须使用的情况
- 常量成员 (
const
) - 引用成员 (
&
) - 没有默认构造函数的成员对象
此外,使用初始化列表也更符合 RAII 原则(资源获取即初始化),提高代码的效率和可读性。
列表初始化(List Initialization)
列表初始化是C++11引入的一种新的初始化方式,使用花括号{}
来初始化变量。它可以用于初始化内置类型、STL容器以及自定义类型。例如:
int arr[] = {1, 2, 3}; // 数组初始化
std::vector<int> vec = {1, 2, 3}; // STL容器初始化
MyClass obj = {1, 2}; // 自定义类型初始化
列表初始化的优点包括: