c++ 为什么在类作用域中定义变量的顺序无关紧要?

9rygscc1  于 2023-06-25  发布在  其他
关注(0)|答案(3)|浏览(163)

如果我们在任何函数中执行这样的两行,我们将得到一个错误:

int a = b;
int b = 0;

这是合乎逻辑的,因为变量b是在初始化a = b之后定义的。
当我们将这两行插入到类的主体中时,为什么没有错误发生,为什么类不关心定义了哪行b,而只定义了b

class Foo
{
    int a = b;
    int b = 0;
};
t5zmwmid

t5zmwmid1#

当你定义两个非成员变量时,它们将被立即初始化。
定义两个成员变量时,初始化将在构造对象时发生,而不是在定义变量时发生。

***但是***初始化是按照声明顺序进行的,所以a的初始化将使用b的未初始化和 * 不确定 * 值,这会导致 * 未定义行为 *。

cu6pst1q

cu6pst1q2#

在类中声明成员的顺序很重要。
顺序决定了它们的初始化顺序。像你的例子一样使用默认的初始化器大致相当于使用这个构造函数和成员初始化器列表:

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
};
uwopmtnx

uwopmtnx3#

我想补充一点:
unqualified name lookup中,“成员函数定义”一节说:
对于成员函数体中使用的名称、成员函数的默认参数、成员函数的异常规范或默认成员初始化器,搜索的范围与类定义中相同,只是类的整个范围被视为,而不仅仅是声明之前使用该名称的部分。
因此,您甚至可以在类中声明b之前在这里使用它。
更新:正如其他答案所说,a是用b初始化的,但在b初始化之前。查看https://godbolt.org/z/MT86nd3Yr,您可以发现a实际上包含一个垃圾值。

相关问题