在C++中,成员变量必须在对象构造完成前初始化,但初始化的方式有多种...
在C++中,成员变量必须在对象构造完成前初始化,但初始化的方式可以有多种,具体取决于成员变量的类型和设计需求。以下是C++中成员变量初始化的规则和相关机制:
1. 成员变量必须初始化
- 如果成员变量是基本类型(如
int
、double
等)或类类型,未显式初始化时,它们的值是未定义的(除非是全局或静态变量,会被默认初始化为零值)。 - 如果成员变量是引用类型或常量成员(
const
),则必须在成员初始化列表中被显式初始化,否则会导致编译错误。
常量成员必须在成员初始化列表中进行显式初始化的原因是很明显的,因为它一旦初始化完成,它的值就不能被修改了,假如编译器允许它不用被显式初始化,那么它在完成初始化后就有一个不确定的值作为其初始化值,而它一旦完成初始化,那么其值就不能被修改了,所以它就只能永远保持一个不确定的值,这样的常量对于我们是没有任何意义的,所以它必须在成员初始化列表中被显示初始化。
而至于为什么引用成员也要在成员初始化列表中被显式初始化,详见 https://blog.csdn.net/wenhao_ir/article/details/145422877
2. 初始化的方式
C++提供了多种初始化成员变量的方式:
(1) 默认初始化
- 如果没有显式初始化,成员变量会使用默认构造函数(如果有)或未定义的值。
- 例如:
class MyClass { int x; // 未显式初始化,值未定义 };
(2) 成员初始化列表(Member Initializer List)
- 在构造函数的初始化列表中显式初始化成员变量。
- 这是推荐的方式,尤其是对于引用类型、常量成员和没有默认构造函数的类类型成员。
- 例如:
class MyClass { public: int x; const int y; int& z; MyClass(int a, int b, int& c) : x(a), y(b), z(c) {} // 使用初始化列表 };
(3) 构造函数体内赋值
- 在构造函数体内对成员变量赋值。
- 这种方式其实不是初始化,而是赋值操作。对于常量成员和引用成员,详细的原因我在前面已经说明了,本质上就是因为二者一旦被初始化,对于常量成员值就不能被更改了,对于引用成员,与别的变量的绑定关系就不能再修改了。
- 例如:
class MyClass { public: int x; MyClass(int a) { x = a; // 赋值,不是初始化 } };
(4) 默认成员初始化器(C++11 引入)
- 在类定义中直接为成员变量提供默认值。
- 例如:
class MyClass { public: int x = 10; // 默认初始化 };
3. 必须使用初始化列表的情况
以下成员变量必须在构造函数的初始化列表中初始化:
- 常量成员(
const
):因为常量成员一旦初始化后就不能修改。 - 引用成员:详细的原因见 https://blog.csdn.net/wenhao_ir/article/details/145422877
- 没有默认构造函数的类类型成员:如果成员是一个类对象,且该类没有使用默认构造函数进行构造,则必须通过初始化列表显式初始化。
例如:
class MyClass {
public:
const int x;
int& y;
MyClass(int a, int& b) : x(a), y(b) {} // 必须使用初始化列表
};
4. 初始化顺序
- 成员变量的初始化顺序与它们在类中声明的顺序一致,而不是初始化列表中的顺序。
- 例如:
class MyClass { public: int x; int y; MyClass(int a) : y(a), x(y) {} // x 会先初始化,但 y 还未初始化,导致 x 的值未定义 };
5. 总结
- 所有成员变量必须在对象构造完成前初始化。
- 初始化可以通过默认初始化、成员初始化列表、构造函数体内赋值或默认成员初始化器实现,默认成员初始化器是C++11引入的。
- 对于常量成员、引用成员和没有使用默认构造函数的类类型成员,必须使用成员初始化列表。