参考【深度解析C++之初始化列表】_new 对象有初始化列表-CSDN博客
初始化列表在构造函数执行之前完成成员的初始化。初始化列表里面初始化的是类对象的成员变量,不仅可以初始化const变量,也可以初始化常规变量。
//v1对m1进行初始化,v1,v2对m2进行初始化,v3对m3进行初始化
ClassName::ClassName():m1(v1), m2(v1,v2), m3(v3)
{
//
}
为什么使用初始化列表?
1.效率
class MyClass {
public:
int x;
double y;
MyClass(int a, double b) {
x = a;
y = b;
}
};
如果我们定义了这样一段代码,那么进行对象初始化的时候就会进行如下操作:
1)利用new关键字进行内存的分配
2)new这个关键字会自动调用构造函数来为对象当中的字段进行初始化
但是当我们使用初始化列表的时候
MyClass(int a, double b) : x(a), y(b) {}
这种方式的好处在于直接创建对象的时候就已经完成了初始化,对象创建好的一瞬间就已经完成了初始化,就不需要再调用构造函数进行逐一初始化了,这里大家可能会有一个误区认为初始化列表是构造函数的一部分,这是大错特错的!!!它们两个虽然在语法结构上定义在了一起,但是其实它们是两个独立的过程,初始化列表对对象进行初始化的时候,构造函数还没有真正地开始执行!所以不需要调用构造函数就能够为对象进行初始化了
2.成员常量变量和引用
对于某些情况下的成员变量,例如常量或者引用,必须使用初始化列表进行初始化,不能够在构造函数的函数体内进行赋值初始化。
常量成员变量
常量成员变量是在声明的时候就被赋予初始值的,并且一旦被赋值就不能够在被修改,所以自然而然地也就不能够再函数体内进行赋值运算,因为一旦进入构造函数体常量成员变量就不能够再被修改。
MyClass(int a) {
// 错误:常量成员变量不能在构造函数体内赋值
x = a;
}
正确写法:
class Example{
private:
int _a;
double _b;
public:
// 使用初始化列表初始化成员变量
Example(int a, double b) : _a(a), _b(b) {
// 构造函数体
}
};
引用成员变量
引用成员变量必须再构造函数的初始化列表当中进行初始化,一旦引用被初始化,它将一直引用同一个对象,无法在构造函数体内重新赋值为另一个对象,如果我们尝试在构造函数体内对引用变量进行赋值,编译器就会报错。
class MyClass {
public:
int& y;
// 错误:引用成员变量必须在初始化列表中初始化
MyClass(int a) {
y = a;
}
};
正确写法:
class Example {
private:
const int _constantValue;
int& _referenceValue;
public:
// 构造函数初始化列表
Example(int constantValue, int& referenceValue)
: _constantValue(constantValue), _referenceValue(referenceValue) {
// 构造函数体
}
};
3.自定义类型,且没有默认构造函数的成员变量必须走初始化列表
对象成员必须使用初始化列表进行初始化,因为如果不在初始化列表当中对这个对象和进行初始化的话,那么Date类的构造函数会自动去调用_t 的默认构造函数来进行初始化,但是Time类没有默认构造函数,编译器就会报错引发未定义行为。所以初始化对象成员要么自动调用默认构造函数,如果没有默认构造函数的话,就必须使用初始化列表进行初始化
默认构造函数是什么?C++当中的默认构造函数一共有三类:1是我们不写编译器自动为我们生成的,2是无参构造函数,3是全缺省参数的构造函数。当我们显示地定义了构造函数编译器就不会为我们生成默认构造函数了,除非我们自己写一个默认构造函数,所以这个时候对象成员初始化的时候如果要依靠编译器自动调用它的默认构造函数,他必须有默认构造函数才可以。如果没有的话那么就必须在初始化列表当中进行初始化才可以。
class Time {
public:
int _hour;
int _minute;
int _second;
// 构造函数
Time(int hour, int minute, int second)
: _hour(hour), _minute(minute), _second(second) {
// 构造函数体
}
};
class Date {
private:
Time _t;
public:
// 使用初始化列表调用 Time 类中的构造函数
Date(int hour, int minute, int second)
: _t(hour, minute, second) {
// 构造函数体
}
};
注意事项
成员变量初始化顺序
初始化列表中的成员变量初始化顺序与它们在类中声明的顺序有关,而不是出现在初始化列表中的顺序。
class B{
private:
int i;
int j;
public:
B(int value):j(value),i(j){
cout << "i = " << i << endl;
cout << "j = " << j << endl;
}
};
int main(){
B objC(10);
return 0;
}
结果如下:
上述程序代码看起来像是要把 j 设初值为 value,再把 i 设初值为 j.问题在于,由于声明次序的原因,初始化列表中的”i (j)”其实比”j(value)”更早执行。但因为j 一开始未有初值,所以i(j)的执行结果导致无法预知其值。就输出随机值。
改进代码:将赋值代码写在函数体中
class B{
private:
int i;
int j;
public:
B(int value):j(value){
i = j;
cout << "i = " << i << endl;
cout << "j = " << j << endl;
}
};
int main(){
B objC(10);
return 0;
}
避免使用未初始化的成员变量
在初始化列表中确保所有成员变量都被初始化,避免使用未初始化的成员变量,尤其是对于常量和引用成员。
常量成员变量必须在初始化列表中进行初始化
引用成员变量
引用成员变量必须在初始化列表中与某个对象绑定。初始化列表是C++中一个重要的概念,尤其在构造函数中的使用,它可以提高代码的效率,同时确保对象在被创建时具有正确的初始状态。