什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
定义
在 C++ 中,初始化列表是一种用于初始化类的数据成员的语法机制。它在构造函数的定义中使用,位于构造函数的参数列表之后,函数体之前,以一个冒号:开始
class MyClass {
private:
int num;
double value;
public:
MyClass(int n, double v) : num(n), value(v) {
}
};
作用
在对象创建时,对成员变量进行初始化,特别是对于const成员变量、引用成员变量和没有默认构造函数的成员对象,必须使用初始化参数列表进行初始化,只能在构造函数中使用。
初始化的顺序和成员变量的顺序一致,和初始化参数列表的顺序无关
class A {
int a;
int b;
int c;
const int d;
int& e;
public:
A() :a(1), c(2), b(c), d(2), e(a) {
cout << a << " " << b << " " << c << endl;//打印结果为1,-839928,2。因为是按照成员变量的顺序进行初始化的
}
A(int a, int b, int c, int d, int e) :a(a), b(b), c(c), d(d), e(e) {}
};
int main() {
A a;
}
初始化列表和在构造函数体内赋值有什么区别?
构造函数是给成员变量赋值的
初始化参数列表是给成员变量初始化的
对特殊类型成员
初始化列表:
对于const成员变量,因为const变量在初始化后不能再被赋值,所以必须使用初始化列表来初始化。
class ConstClass {
private:
const int const_num;
public:
ConstClass(int n) : const_num(n) {
}
};
引用类型的成员变量也必须在初始化列表中初始化。引用在定义后不能重新绑定到其他对象,所以需要在初始化时确定其引用的对象
class RefClass {
private:
int& ref_num;
public:
RefClass(int& n) : ref_num(n) {
}
};
构造函数体内赋值:
无法用于初始化const成员和引用成员。因为在构造函数体执行时,const成员已经被默认初始化(不符合要求),引用成员如果没有在初始化列表初始化,在构造函数体中就无法再进行合法的初始化。
性能差异
初始化列表:
对于包含其他类对象作为成员(成员对象)的情况,初始化列表可以更高效地初始化这些成员对象。如果在初始化列表中调用成员对象的构造函数,成员对象的构造函数只会被调用一次,按照正确的顺序初始化。
假设有一个类Container包含一个std::vector成员vec,在初始化列表中初始化vec可以避免不必要的默认构造和重新赋值操作
class Container {
private:
std::vector<int> vec;
public:
Container() : vec(10) { // 直接初始化为包含10个元素的vector
}
};
构造函数体内赋值:
如果先默认构造成员对象,然后在构造函数体内赋值,可能会导致额外的性能开销。对于一些资源密集型的对象,如动态分配内存的对象,这可能会涉及到多次内存分配和释放操作。例如,如果在构造函数体内给std::vector成员赋值,可能会先默认构造一个空的vector,然后再重新分配内存来存储新的值,这比直接在初始化列表中初始化效率要低。
对于一些需要在构造函数体内进行复杂计算的成员变量,可以先用初始化列表中进行默认初始化,然后再构造函数体内进行赋值。
class Rectangle {
private:
double length;
double width;
double area;
public:
Rectangle(double l, double w) : length(l), width(w), area(0) {
// 在构造函数体内进行复杂计算(这里是计算面积)
area = length * width;
}
double getArea() {
return area;
}
};
在初始化列表中对length和width进行了初始化,同时对area进行了默认初始化(初始化为 0)。这是因为在进入构造函数体之前,我们还没有计算出area的值,所以先给它一个默认值。