C++构造函数的初始化列表 到底是什么作用?
·
前言:C++构造函数的初始化列表,你真的懂它吗?
很多C++初学者,一般都会将构造函数的参数列表与初始化列表混为一谈。今天,小栈帮大家真正读懂构造函数的初始化列表。
目录
一、先看两个例子
例1:
class Box {
public:
Box(int l, int w, int h) { // 这是参数列表
// 这是在函数体内进行“赋值”操作
length = l;
width = w;
height = h;
}
private:
int length;
int width;
int height;
};
在这个示例中,我们是在函数体内为成员赋值的,它实际上分为两步:
1.在进入函数体{}之前,成员已经默认初始化了
2.进入函数体之后,再通过赋值操作符"="进行赋值操作
例2:
class Box {
public:
// 参数列表 成员初始化列表
Box(int l, int w, int h) : length(l), width(w), height(h) {
// 函数体可以为空,或者执行其他逻辑
}
private:
int length;
int width;
int height;
};
在这个示例中,我是在函数体外,通过初始化列表对成员直接进行初始化的。这种初始化列表的方式是一步到位的。
二、初始化列表的优势:
从上述两个示例中,不难发现,构造函数的初始化列表更加高效,它将原先函数体内赋值的两步操作默认初始+赋值),变成直接初始化。
三、初始化列表存在的必要性:
初始化列表的存在不仅仅是为了提高效率,有些情况下,我们必须使用初始化列表的方式。例如,以下几种case:
Case1: const成员变量
const类型的变量不能做赋值操作。
class AA {
const int a;
public:
// ❌ 错误:const 不能先默认构造再赋值
AA(int val) {
a = val; // 编译错误:const 成员不可赋值
}
// ✅ 正确:直接初始化
AA(int val) : a(val) {}
};
Case2: 引用成员变量(&)
引用类型的变量不能做赋值操作。
class AA {
public:
AA(int& age_ref) {
age = age_ref; // ❌ 编译错误:引用不是可修改的左值
}
// ✅ 正确:直接初始化
AA(int& age_ref) : age(age_ref) {}
private:
int& age; // 引用成员
};
Case3: 在子类构造函数中,调用父类的构造函数
class Base {
public:
Base(int x); // 带参构造,无默认构造
};
class Derived : public Base {
public:
// 必须通过初始化列表调用 Base(10)
Derived() : Base(10) {}
};
四、初始化顺序
需要注意一个陷阱,构造函数的初始化顺序是由类中成员的声明顺序决定,与初始化列表书写顺序无关。
典型的错误示例:
class AA {
int a;
int b;
public:
AA(int x) : b(x), a(b + 1) {} // 危险!! !
};
上述代码的问题在于,成员a先于成员b进行初始化,此时b的值是垃圾值。
所有评论(0)