C++ 成员初始化机制总结笔记
1. 成员初始化的三种主要写法
以非静态成员为例,常见三种方式:
// 方式 1:声明处默认初始化
class A {
public:
A(); // 可以有多个构造函数
private:
int a = 0;
int b{1};
};
// 方式 2:构造函数初始化列表
class B {
public:
B()
: a(0)
, b(1)
, c{2, 3}
{}
private:
int a;
int b;
int c[2];
};
// 方式 3:构造函数体内赋值
class C {
public:
C()
{
a = 0;
b = 1;
}
private:
int a;
int b;
};
三种方式都合法,但语义和推荐程度不同。
2. 成员声明处默认初始化(in-class member initializer)
2.1 写法示例
class Test {
public:
Test(); // 构造函数可以不管 a、b
private:
int a = 0;
int b{1};
};
2.2 适用范围
-
适用于「非静态成员」:
- 内置类型:
int a = 0; - 类类型:
std::string s = "hello"; const成员:const int x = 42;- 引用成员:
int& r = someGlobal;
- 内置类型:
-
不适用于普通
static数据成员
静态成员不属于某个对象实例,它有单独的初始化规则(见后面)。
2.3 作用规则
对于每个构造函数:
- 如果构造函数初始化列表中没有提到该成员:
- 使用声明处的默认初始化式。
- 如果构造函数初始化列表中显式初始化该成员:
- 以初始化列表为准,声明处默认值对这个构造函数无效。
示例:
class Test {
public:
Test() = default; // 使用 a = 0
Test(int x) : a(x) {} // 使用 a(x)
private:
int a = 0;
};
Test t1;→a初始化为 0Test t2(5);→a初始化为 5
2.4 使用建议
- 表达「这个成员的通常默认状态」时,可以写在声明处。
- 多个构造函数都需要同一个默认值时,声明处初始化能减少重复。
3. 构造函数初始化列表
3.1 写法示例
class Test {
public:
Test()
: a(0)
, b(1)
, c{2, 3}
{}
private:
int a;
int b;
int c[2];
};
3.2 适用范围
可以在初始化列表中初始化:
- 直接基类子对象:
class Base {}; class Derived : public Base { public: Derived() : Base() {} }; - 非静态成员(包括):
- 普通成员
const成员- 引用成员
- 无默认构造函数的类成员
不能在初始化列表中初始化:
static成员(它们不属于具体对象实例)。
3.3 初始化列表 vs 声明处默认值
如果两者同时存在:
class Test {
public:
Test() : a(2) {} // 初始化列表
private:
int a = 1; // 声明处默认值
};
行为是:
- 对
Test::Test()这个构造函数来说:a只会被初始化一次,用的是a(2)。a = 1这条默认初始化式在这个构造函数里被忽略。
不会出现“先按声明处初始化为 1,再按初始化列表改为 2”这种两次初始化。
可以总结为:
对于同一个成员,编译器在生成构造函数时只会为它选择一个初始化式:
优先用初始化列表,缺失时才用声明处默认值。
4. 构造函数体内赋值
4.1 写法示例
class Test {
public:
Test()
{
a = 0;
b = 1;
}
private:
int a;
int b;
};
4.2 实际发生了什么
对于 a、b:
- 在进入构造函数体前,成员已经被「默认初始化」:
- 对内置类型(
int等):值未定义(不自动变 0)。 - 对类类型:调用默认构造函数。
- 对内置类型(
- 在构造函数体内,你再执行一次赋值操作(
operator=)。
对 int 这样的标量,最终结果是你期待的值,但中间有一段「未定义值」的阶段。
对 std::string 等类类型,则多了一次「构造 + 赋值」,不如初始化列表一次构造干净。
4.3 使用建议
- 不推荐用构造函数体内赋值来做「第一次初始化」。
- 更适合做后续逻辑处理,而不是成员的初始构造。
5. static 成员单独说明
static 数据成员的初始化规则与普通成员不同:
-
非
inline的静态数据成员,一般需要在类外定义并初始化:class A { public: static int counter; }; int A::counter = 0; // 类外定义 + 初始化 -
C++17 引入
inline static,可以在类内直接定义并初始化:class A { public: inline static int counter = 0; };
静态成员不参与每个对象的构造/析构,所以:
- 与「成员声明处默认值 / 初始化列表」的讨论不是同一维度的问题;
- 初始化列表不能初始化静态成员。
6. 实践建议小结
可以整理成几条简单的习惯:
-
成员的第一次合法状态
- 优先在「成员声明处 + 初始化列表」完成。
- 不要依赖构造函数体内赋值做首次初始化。
-
声明处默认值
- 表达成员的「默认状态」,对所有构造函数生效。
- 某个构造函数需要特殊值时,用初始化列表覆盖。
-
初始化列表
- 用于基类、
const成员、引用成员、无默认构造的成员。 - 用于类类型成员时,一步构造,避免「默认构造 + 赋值」的多余操作。
- 用于基类、
-
顺序
- 成员构造/析构顺序只看「声明顺序」,不看初始化列表写的顺序。
- 有依赖关系时,按照依赖关系调整声明顺序。
-
static 成员
- 把它当成「类作用域的全局变量」,按静态对象规则初始化。
- 不参与普通对象的构造/析构,不在初始化列表里出现。
按这套规则写代码,既能避免初始化顺序/重复操作的误解,又能把成员初始化的行为控制在预期之内。

浙公网安备 33010602011771号