目录
- 01-C++类的初始化列表的概要介绍
- 语法
- 为什么使用初始化列表?
- 示例代码
- 小结
- 02-初始化列表是写在构造函数的函数体之前的代码的
- **1. 结构分析**
- **2. 示例:基类 + 成员变量**
- **输出**
- **3. 初始化列表 vs 构造函数体**
- **(1) 使用初始化列表**
- **(2) 在构造函数体赋值**
- 03-必须放在初始化列表中进行初始化的东西
- 常量成员
- 引用成员
- 没有默认构造函数的类成员
- 类的基类没有默认构造函数
01-C++类的初始化列表的概要介绍
在C++中,类的初始化列表是一个在构造函数体之前用来初始化类成员的机制。它允许你在构造函数的声明部分直接初始化成员变量,而不是在构造函数体内通过赋值的方式。
语法
初始化列表紧跟在构造函数名称之后,用冒号 :
开始,后面跟着成员变量的初始化。每个成员变量通过逗号 ,
分隔。
class ClassName {
public:// 构造函数带有初始化列表ClassName(int a, int b) : x(a), y(b) { // x和y分别通过初始化列表初始化// 其他构造函数体代码(如果有)}private:int x, y;
};
其中x(a)
的意思并不是调用名叫x的函数,而是把成员变量x的值初始化a的值。同样,y(b)
的意思也并不是调用名叫y的函数,而是把成员变量y的值初始化为b的值。
为什么使用初始化列表?
- 提高效率:通过初始化列表可以直接初始化类成员,而不是先调用默认构造函数再进行赋值操作。
- 有一些成员变量只能在初始化列表中进行初始化,具体如下:
①常量成员和引用成员:常量成员变量和引用成员必须通过初始化列表来初始化,因为它们无法在构造函数体内被赋值。至于为什么,请往后看,我在后面说明了原因。
②如果类型为类类型的成员变量【即某个类的实例对象】没有默认构造函数,也需要在初始化列表中进行初始化。关于什么叫默认构造函数,请看我的另一篇博文 [https://blog.csdn.net/wenhao_ir/article/details/145428240] - 如果一个类的基类没有使用默认构造函数,也需要在初始化列表进行初始化。关于什么叫默认构造函数,请看我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145428240
示例代码
#include <iostream>
using namespace std;class Rectangle {
public:// 带有初始化列表的构造函数Rectangle(int l, int w) : length(l), width(w) {// 构造函数体cout << "Rectangle created!" << endl;}// 打印矩形的面积void area() {cout << "Area: " << length * width << endl;}private:int length, width;
};int main() {Rectangle rect(10, 5); // 创建一个长为10,宽为5的矩形rect.area(); // 打印矩形的面积return 0;
}
关键点:
Rectangle(int l, int w) : length(l), width(w)
:初始化列表在这里将length
和width
直接初始化为传入的l
和w
参数的值。length
和width
是在构造函数体内使用之前就已经初始化了,而不是在函数体内通过赋值。
小结
- 初始化列表通过冒号
:
开始,直接在构造函数声明中初始化成员变量。 - 它比在构造函数体内赋值更高效,尤其对于常量成员、引用成员或需要特殊初始化的成员非常有用。
02-初始化列表是写在构造函数的函数体之前的代码的
初始化列表(Initializer List)是写在构造函数的函数体 {}
之前,用于初始化成员变量和基类。它的语法如下:
ClassName::ClassName(参数列表) : 成员变量1(值1), 成员变量2(值2), 基类(值) { // 构造函数体
}
1. 结构分析
class Example {int a;double b;
public:Example(int x, double y) // 构造函数: a(x), b(y) // ✅ 初始化列表(在函数体 `{}` 之前){// 构造函数体}
};
-
: a(x), b(y)
是 初始化列表,用来初始化a
和b
。其中a(x)
的意思并不是调用名叫a的函数,而是把成员变量a的值初始化x的值。同样,b(y)
的意思也并不是调用名叫b的函数,而是把成员变量b的值初始化为y的值。 -
{}
内是构造函数体,在初始化列表执行完之后才会运行。
2. 示例:基类 + 成员变量
#include <iostream>class Base {
public:Base(int x) { std::cout << "Base constructor: " << x << "\n"; }
};class Derived : public Base {int a;
public:Derived(int x, int y): Base(x), a(y) // ✅ 初始化列表{std::cout << "Derived constructor: " << a << "\n";}
};int main() {Derived d(10, 20);
}
输出
Base constructor: 10
Derived constructor: 20
流程
- 先执行初始化列表
Base(x)
先调用Base
的构造函数,输出"Base constructor: 10"
。a(y)
赋值给成员变量a
,即把y的值传递给成员变量a,注意这里不是调用函数名为a的函数。
- 然后执行构造函数体
{}
- 输出
"Derived constructor: 20"
。
- 输出
上面代码中类Derived在定义时继续了基类Base,那么请问public Base
中public的意思是什么?
答案见:https://blog.csdn.net/wenhao_ir/article/details/145428506
3. 初始化列表 vs 构造函数体
(1) 使用初始化列表
class Example {int x;
public:Example(int value) : x(value) {} // ✅ 直接初始化
};
(2) 在构造函数体赋值
class Example {int x;
public:Example(int value) { x = value; } // 在构造函数中赋值,不推荐
};
✅ 推荐使用初始化列表,避免在构造函数中赋值,从而提高效率。
03-必须放在初始化列表中进行初始化的东西
常量成员
由于C++规定,所有成员变量必须在对象构造完成前初始化。而常量成员在初始化后不能修改,所以只能用初始化列表进行初始化。例子如下:
class Test {const int x;public:Test(int val) : x(val) {} // 常量成员x必须用初始化列表进行初始化,否则编译错误};
引用成员
关于引用成员为什么也必须在初始化列表中进行初始化,详情见 https://blog.csdn.net/wenhao_ir/article/details/145422877
其实原因也和常量成员差不多,只是你需要了解下C++的引用成员是怎么回事儿。
例子如下:
class Test {int& ref; // ref是一个引用类型的成员public:Test(int& r) : ref(r) {} // ref必须用初始化列表进行初始化};
没有默认构造函数的类成员
如果某个成员变量的类型为类类型,即这个成员变量是一个类的对象实例,而这个类又没有默认构造函数,那么也需要在初始化列表中进行初始化。
详情见 https://blog.csdn.net/wenhao_ir/article/details/145428240
类的基类没有默认构造函数
如果一个类的基类没有使用默认构造函数,也需要在初始化列表进行初始化。详情请请看我的另一篇博文 https://blog.csdn.net/wenhao_ir/article/details/145428240