默认值在结构体中的作用以及构造函数的顺序

3

我知道我们可以为结构体成员设置默认值。例如,我可以为这些成员设置默认值:

struct Foo {
  int a = 0;
  int b = 1;
  int c;
}

假设我有一个成员 c 的另一个构造函数:
struct Foo {
  int a = 0;
  int b = 1;
  int c;
  foo(int input_c): c(input_c) {}
}

在这种情况下,当我构建一个Foo时,构造的顺序是什么?如果我执行

Foo(100)

我的理解是a和b都会被默认构造,然后c被赋予了100,这样说是否正确?

------------- 更新 ---------------------

我有些困惑的地方还在于执行顺序。对于默认值,它们是在构造函数之前就已经被执行吗?

例如,我可以修改我的Foo

struct Foo {
  int a = 0;
  int b = 1;
  int c = -1;
  foo(int d) {
    c += d;     // Does c always started with -1?
  }
}

我之前已经回答过这个问题了,等一下。 - Asteroids With Wings
1
初始化顺序始终是类/结构体中成员声明的顺序。 - Jesper Juhl
@JesperJuhl 是的,但这里有两个问题:成员的顺序和构造函数。我可以有多个构造函数,那么构造函数的顺序呢? - WhatABeautifulWorld
@WhatABeautifulWorld 不重要。此外,在成员初始化列表中列出事物的顺序无关紧要。如果成员按照a,b,c的顺序声明,那么它们将按照这个顺序进行初始化。没有任何if,但是或其他任何东西。 - Jesper Juhl
@WhatABeautifulWorld 我决定发表一个答案而不是另一个评论。请查看那个。 - Jesper Juhl
显示剩余2条评论
2个回答

4

成员变量的初始化顺序非常严格,它总是按照成员声明的顺序进行。

首先,在类内默认初始化器被应用(按照成员声明的顺序),除非被初始化列表覆盖。

然后使用构造函数初始化列表,并且其中列出的任何成员都按照其声明的顺序进行初始化。如果其中任何列出的成员也具有类内初始化器,则这些初始化器将不会发生作用,初始化列表获胜并用于初始化成员。

接下来执行构造函数体。此时所有成员变量已经初始化,无论是通过类内初始化还是初始化列表。但是构造函数体可以选择为这些已初始化的成员变量分配新值。

无论如何,对于给定的成员变量foo,它将被类内初始化(如果有)或初始化列表(如果有)初始化,或者将进行默认初始化。无论使用哪种方法来初始化它,在其前面声明的另一个成员之后,在其后面声明的另一个成员之前始终会发生该初始化。

例如:

struct s {
    int a = 1;
    int b = 42;
    int c = 666;
    int d;
    int e;
    s() : e(3), b(123) {
        c = 7;
    }
};

上面的代码将始终首先初始化变量 a,由于它有一个类内初始值设定项,因此将使用它,其值为1
变量 b 第二个被初始化,它有一个类内初始值设定项,但构造函数的初始化列表会覆盖它。它将被初始化为值 123
然后变量 c 被初始化为值 666
变量 d 未初始化/或者可以说是默认初始化,对于 int 类型来说,这与未初始化相同,但对于其他类型(如std::string)则意味着初始化为空字符串 - 这取决于类型是否具有可用值。
接下来变量e 被初始化为值3。这是最后发生的,因为它是最后声明的。在构造函数的初始化列表中列出的顺序是无关紧要的。
然后执行构造函数体并将 c 赋值为 7。所以它既被初始化又被赋值了 - 这通常是低效的。
现在对象构造已经完成。

你的第一段与接下来的两个段落相矛盾(你仅构建了一个示例,但事实并不如此)。如果 d 具有等于或大括号初始化程序,而 b 没有,则根据你的第一个陈述,顺序仍然是abcde,但根据你回答的后面部分,顺序变为acdbe。 - Ben Voigt
@Ben 我不太明白我的回答为什么不清楚(请随意编辑)。初始化顺序始终是abcde,但是任何给定元素是否由类内值或初始化列表初始化取决于初始化列表中是否有条目。如果有,它总是获胜,如果没有,则使用类内初始化。如果我没有表达清楚,请随意编辑和改进我的措辞。或者如果我完全搞砸了什么,请告诉我。 - Jesper Juhl
我不确定如何在完全破坏你的句子结构的情况下使其清晰明了。问题在于,你将初始化器作为一组进行讨论,将具有初始化器的成员作为一组,将在构造函数初始化器列表中提到的成员作为另一组。实际上,并没有这些分组,每个成员(实际上是每个子对象,基类实例在成员之前被处理)都会单独完整地处理,然后再移动到下一个成员并对其应用相同的规则。 - Ben Voigt
@Ben 是的,我同意。每个成员(或基础对象,首先)都按照它们声明的顺序单独初始化。它们被初始化的值要么是从构造函数初始化列表中获得的,要么是在类内初始化器中指定的。但所有这些都是按照声明顺序发生的。如果我发布的答案不清楚,我很抱歉。我希望评论能够纠正这一点。我相信我知道这是如何工作的,但我可能缺乏表达它的语言技巧。如果您认为它仍然不清楚或具有误导性,请对答案进行负投票。 - Jesper Juhl

3

是的,成员将按照它们在类中声明的顺序进行初始化。

因此,当您调用 Foo(100) 时,ab 将使用默认值进行初始化,然后 c 将被初始化为 100


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