

























以非静态成员为例,常见三种方式:
// 方式 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;
};
三种方式都合法,但语义和推荐程度不同。
class Test {
public:
Test(); // 构造函数可以不管 a、b
private:
int a = 0;
int b{1};
};
适用于「非静态成员」:
int a = 0;std::string s = "hello";const 成员:const int x = 42;int& r = someGlobal;不适用于普通 static 数据成员
静态成员不属于某个对象实例,它有单独的初始化规则(见后面)。
对于每个构造函数:
示例:
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 初始化为 5class Test {
public:
Test()
: a(0)
, b(1)
, c{2, 3}
{}
private:
int a;
int b;
int c[2];
};
可以在初始化列表中初始化:
class Base {};
class Derived : public Base {
public:
Derived() : Base() {}
};
const 成员不能在初始化列表中初始化:
static 成员(它们不属于具体对象实例)。如果两者同时存在:
class Test {
public:
Test() : a(2) {} // 初始化列表
private:
int a = 1; // 声明处默认值
};
行为是:
Test::Test() 这个构造函数来说:
a 只会被初始化一次,用的是 a(2)。a = 1 这条默认初始化式在这个构造函数里被忽略。不会出现“先按声明处初始化为 1,再按初始化列表改为 2”这种两次初始化。
可以总结为:
对于同一个成员,编译器在生成构造函数时只会为它选择一个初始化式:
优先用初始化列表,缺失时才用声明处默认值。
class Test {
public:
Test()
{
a = 0;
b = 1;
}
private:
int a;
int b;
};
对于 a、b:
int 等):值未定义(不自动变 0)。operator=)。对 int 这样的标量,最终结果是你期待的值,但中间有一段「未定义值」的阶段。
对 std::string 等类类型,则多了一次「构造 + 赋值」,不如初始化列表一次构造干净。
构造顺序:按「成员在类中声明的顺序」构造。
析构顺序:按「声明顺序的反向」析构。
示例:
class Holder {
public:
Holder()
: r1("r1")
, r2("r2")
{}
private:
FakeResource r1;
FakeResource r2;
};
无论你在初始化列表里先写 r2 还是 r1,真正的顺序始终是:
r1,后 r2r2,后 r1static 数据成员的初始化规则与普通成员不同:
非 inline 的静态数据成员,一般需要在类外定义并初始化:
class A {
public:
static int counter;
};
int A::counter = 0; // 类外定义 + 初始化
C++17 引入 inline static,可以在类内直接定义并初始化:
class A {
public:
inline static int counter = 0;
};
静态成员不参与每个对象的构造/析构,所以:
可以整理成几条简单的习惯:
成员的第一次合法状态
声明处默认值
初始化列表
const 成员、引用成员、无默认构造的成员。顺序
static 成员
按这套规则写代码,既能避免初始化顺序/重复操作的误解,又能把成员初始化的行为控制在预期之内。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。