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

8
如果我们在任何函数中执行这两行代码,我们将会得到一个错误,这是合理的,因为变量b在初始化a = b之后才被定义。
int a = b;
int b = 0;

但是当我们将这两行代码插入到类的范围内时,为什么类不关心b的定义顺序呢?
class Foo
{
    int a = b;
    int b = 0;
};

1
顺便说一下,structclass只是用来定义类的两个关键字。唯一的区别在于成员的默认访问权限。 - 463035818_is_not_an_ai
1
顺便说一下,structclass只是用来定义类的两个关键字。唯一的区别在于成员的默认访问权限。 - undefined
1
请注意,大多数编译器都会对这个错误发出警告:https://godbolt.org/z/YfMGo9P8j - Alan Birtles
1
请注意,大多数编译器都会对这个错误发出警告:https://godbolt.org/z/YfMGo9P8j - undefined
坦白说,你之前已经接受了一个答案,现在又开始悬赏以获取更详细的回答,这有点奇怪。你能指出现有答案中你所缺少的内容吗? - 463035818_is_not_an_ai
@463035818_is_not_an_ai 我想要更详细地了解编译器在类中如何进行初始化的过程。 - Jibel
3个回答

14
在类中声明成员的顺序确实很重要。
这个顺序决定了它们被初始化的顺序。使用像你的例子中那样的默认初始化器,大致相当于使用带有成员初始化列表的构造函数:
class Foo
{
    int a;
    int b;
    Foo() : a(b) , b(0) {}   // !! undefined !!
};

在这里,初始化的顺序仍然由成员声明的顺序决定,而不是成员初始化列表中的顺序。当顺序不同时,编译器通常会发出警告。然而,在上述情况中问题变得更加明显:ab 初始化之前就被初始化了。在初始化之前读取 b 的值是未定义行为。
要正确地将一个成员初始化为另一个成员的值,您必须遵守顺序:
class Foo
{
    int b = 0;  // initialized first
    int a = b;  // OK
};

13
当你定义两个非成员变量时,它们将立即被初始化。
当你定义两个成员变量时,初始化将在对象构造时发生,而不是在变量定义时发生。
然而,初始化按照声明顺序进行,所以变量a的初始化将使用未初始化和不确定的变量b的值,这会导致未定义行为。

所以这种初始化方式总是给a赋错值吗? - Jibel
这种初始化方式总是给a赋予错误的值吗? - undefined
3
@Jibel 不总是这样。如果你交换它们的声明,它们就按正确的顺序初始化,一切都没问题。 - 463035818_is_not_an_ai
3
@Jibel 不总是。如果你交换它们的声明,它们就会按正确的顺序初始化,一切都会没问题。 - undefined

7

我想要补充一点:

未限定名称查找中,"成员函数定义"部分说:

对于在成员函数体内使用的名称、成员函数的默认参数、成员函数的异常规范或者默认成员初始化器,搜索的范围与类定义中相同,不同之处在于考虑整个类的作用域,而不仅仅是在使用该名称之前的部分。

因此,在这里你甚至可以在类中声明之前使用b


更新:正如其他答案所说,a 是用 b 初始化的,但是在 b 初始化之前。请参考 https://godbolt.org/z/MT86nd3Yr 你会发现 a 实际上保存了一个垃圾值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接