C++构造函数初始化列表相关技术疑问解答
先贴出代码:
ClssDoAction::ClssDoAction() : var1(0), // declared as int var2(0), // declared as int var3(0) // declared as int { var4[0] = 0; // declared as char var5[5] = 0; // declared as char var6 = 128; // declared as int var7 = NULL; // declared as char } ClssDoAction::~ClssDoAction() { }
首先明确:构造函数后的冒号部分是成员初始化列表,属于C++类成员的标准初始化方式,和花括号内的赋值操作逻辑不同。
以下是针对疑问的逐一解答:
1. 为何采用这种变量初始化方式?
成员初始化列表是在成员变量创建阶段直接完成赋值,比构造函数体内的赋值效率更高——尤其是自定义类对象成员,能避免先默认构造再赋值的额外开销。另外,const成员、引用成员这类必须在创建时就确定值的变量,只能用初始化列表初始化,没法在构造函数体内赋值。就算是int这类基本类型,用初始化列表也是遵循最佳实践,保持代码初始化逻辑的一致性。
2. 变量var1、var2、var3后标注(0)的原因是什么?
这是C++对基本类型的值初始化写法,作用是把这三个int成员在创建时就初始化为0。对于int这类内置类型,var1(0)和构造函数体内的var1 = 0效果看起来一样,但本质上前者是初始化(变量诞生时就赋予值),后者是赋值(变量先默认创建再修改值)。
3. 构造函数后的冒号起到什么作用?
这个冒号是成员初始化列表的起始标记,语法上用来区分构造函数的参数区和成员初始化区,告诉编译器接下来的内容是在对象创建时直接初始化成员变量,而非在构造函数体内执行常规的赋值语句。
4. 为何冒号后仅初始化整型变量,而花括号内既有整型又有字符型?冒号区域只能初始化整型吗?为何不将所有变量都放在冒号后处理?
首先,初始化列表绝对不是只能初始化整型,它可以处理任何类成员,包括字符型、数组、自定义类型等。这段代码的写法大概率是历史习惯或开发者的选择,具体原因如下:
- var4、var5是数组,初始化列表可以初始化整个数组(比如
var4{0}),但这里只给数组的单个元素赋值,这种属于对已初始化数组的修改操作,没法在初始化列表里完成,只能放在构造函数体内; - var6是int,放在体内赋值是可行的,但不如初始化列表高效,可能是后续新增变量时没按统一写法调整;
- var7是char,用
NULL赋值其实不规范(NULL是指针类型的宏),它也可以用初始化列表写成var7(0),放在体内是赋值操作而非初始化。
总结:初始化列表负责的是成员本身的初始化,而构造函数体内是对已初始化的成员做赋值或修改,像数组单个元素赋值这类操作只能在体内完成。
5. 从Git历史记录中发现冒号后变量顺序曾被修改,该顺序是否有实际意义?是否需要与类中成员变量的声明顺序一致?
初始化列表里的书写顺序不决定实际初始化顺序——成员变量的初始化顺序是严格按照类中声明的顺序执行的,和初始化列表的书写顺序无关。比如如果类里先声明var2再声明var1,那不管初始化列表里写var1在前还是var2在前,都会先初始化var2。
修改初始化列表的书写顺序本身不会改变程序逻辑,但为了避免阅读代码时产生误解,最佳实践是让初始化列表的顺序和类成员的声明顺序保持一致。
内容的提问来源于stack exchange,提问作者old.dank.coder

