如果我们在任何函数中执行这两行,我们都会得到一个错误,这是合乎逻辑的,因为变量
b
是在初始化之后定义的a = b
:
int a = b;
int b = 0;
但是当我们将这两行插入到类的作用域中时,为什么类不关心
b
的定义顺序?
class Foo
{
int a = b;
int b = 0;
};
在类中声明成员的顺序确实很重要。
顺序决定了它们初始化的顺序。使用示例中的默认初始值设定项大致相当于将此构造函数与成员初始值设定项列表一起使用:
class Foo
{
int a;
int b;
Foo() : a(b) , b(0) {} // !! undefined !!
};
这里,初始化的顺序仍然是由声明成员的顺序决定的,而不是由它们在成员初始值设定项列表中的顺序决定的。当顺序不同时,编译器通常会发出警告。不过,在上面的问题变得更加明显:在初始化
a
之前,用 b
初始化 b
。在初始化之前读取 b
是未定义的行为。
要使用另一个成员的值正确初始化一个成员,您必须遵守顺序:
class Foo
{
int b = 0; // initialized first
int a = b; // OK
};
当你定义这两个非成员变量时,它们会立即被初始化。
定义两个成员变量时,初始化将在构造对象时进行,而不是在定义变量时进行。
但是初始化是按照声明顺序进行的,因此
a
的初始化将使用b
的未初始化和不确定值,这会导致未定义的行为。
我想补充一点:
在非限定名称查找中,“成员函数定义”部分说:
对于成员函数体内使用的名称、成员函数的默认参数、成员函数的异常规范或 默认成员初始值设定项 ,搜索的范围与类定义中的相同,除了 整个考虑类的范围,而不仅仅是使用该名称的声明之前的部分。
所以你甚至可以在类中声明之前使用
b
。
更新:正如其他答案所说,
a
是用b
初始化的,但在初始化b
之前。参见 https://godbolt.org/z/MT86nd3Yr,你会发现 a
实际上持有垃圾值。