4

在进行了大量测试并编写了关于如何在 C++ 中将结构初始化为零的答案之后(注意:对它的否决是我完全重写它之前),我不明白为什么= {0}不设置所有成员结构为零!

如果你这样做:

struct data_t
{
    int num1 = 100;
    int num2 = -100;
    int num3;
    int num4 = 150;
};

data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
       d3.num1, d3.num2, d3.num3, d3.num4);

...输出是:

d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150

...虽然我希望输出是这样的:

d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0

...这意味着只有 FIRST 成员设置为零,其余的都设置为默认值。

我一直认为以这三种方式中的任何一种初始化结构都会对其进行零初始化,但显然我错了!

  1. data_t d{}
  2. data_t d = {}
  3. data_t d = {0}

因此,我从这个答案中得到的关键结论是:

这里最大的收获是这些 NONE: data_t d{},data_t d = {}data_t d = {0}, 实际上将结构的所有成员设置为零!

  1. data_t d{}将所有值设置为结构中定义的默认值。
  2. data_t d = {}还将所有值设置为其默认值。
  3. 并且data_t d = {0}仅将 FIRST 值设置为零,并将所有其他值设置为其默认值。

那么,为什么不初始化 C++ 结构以= {0}将其所有成员设置为 0?

请注意,我上面的关键要点实际上与我多年来一直使用的这个看起来相当官方的文档相矛盾(https://en.cppreference.com/w/cpp/language/zero_initialization),它说T t = {} ;并且T {} ;都是零初始化器,事实上,根据我上面的测试和外卖,它们不是。

参考:

  1. 如何在 C++ 中将结构初始化为 0
  2. 更新:我刚刚也指向了这个引用:初始化对象时 {0} 是什么意思?
4

3 回答 3

8

那么,为什么不将 C++ 结构初始化为 = {0} 将其所有成员设置为 0?

因为您只提供一个值,而该类有多个成员。

当您拥有T t{};T t = {}正在做的事情称为值初始化。在值初始化中,如果对象/成员没有默认构造函数或默认成员初始化器,则编译器会回退到零初始化对象/成员。所以随着

data_t d{}

按顺序排列的成员的值将是 100, -100, 0 ,150 并且发生这种情况是0因为num3它没有默认值并且您没有在其中提供值,{}因此编译器会回退到零初始化num3。这与data_t d = {}. 与data_t d = {0}您提供第一个元素num1一样0,但是就像前两个一样,所有其他成员都使用其默认值初始化,如果它们有一个,或者如果没有,则初始化为零,给您 0, -100, 0 , 150 为成员值。

这是在 C++11 发布并允许默认成员初始化程序时发生的更改。


如果你data_t的定义像

typedef struct
{
    int num1;
    int num2;
    int num3;
    int num4;
} data_t;

那么data_t d{}, data_t d = {},data_t d = {0}都会给你留下一个零初始化的类,因为没有默认的成员初始值设定项,并且你在括号初始化列表(的技术名称{...})中提供的唯一值为零,因此所有成员都为零。

于 2020-04-30T20:24:51.370 回答
5

data_t d3 = {0}列表初始化语法,它与聚合如data_t执行聚合初始化:提供的0值用于初始化第一个成员,其余成员使用其相应的默认值进行初始化,如果不存在,则进行值初始化强调我的,为 C++14 编辑):

如果初始化子句的数量少于成员的数量或初始化列表完全为空,则其余成员将由其默认成员初始化器(如果在类定义中提供)进行初始化,否则按照通常的列表由空列表初始化-初始化规则(使用默认构造函数对非类类型和非聚合类执行值初始化,并为聚合执行聚合初始化)。如果引用类型的成员是这些剩余成员之一,则程序格式错误。

值初始化意味着对非类类型进行零初始化。就是为什么num3没有默认值的成员获取值的0

注意:不要与default-initialization混淆,它根本不初始化非类类型。data_t d3;将是default-initialization,并且该成员num3将处于不确定状态。

需要注意的重要一点是正在初始化的对象是否是聚合,因为聚合与具有构造函数的类的初始化规则不同。在构造函数的情况下,没有默认值的非类成员将被默认初始化(即处于不确定状态)。

一些例子:

struct A { // an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
};

struct B { // not an aggregate
    int num1 = 100;
    int num2 = -100;
    int num3;
    B() {}
    B(int) {}
};

int main() {
    A a1; // default-initialization: a1 is {100, -100, ???}
    A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
    A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
    A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
    B b1; // default-initialization: b1 is {100, -100, ???}
    B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
    B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
    B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}

另请注意,聚合初始化规则早于 C++11。例如,请参阅这个相关的 C++11 之前的问题:初始化对象时 {0} 是什么意思?

于 2020-04-30T20:24:44.273 回答
2

简短的回答:因为你有类内初始化器。

更长的答案:因为您正在为 C++14 或更高版本进行编译,所以具有类内初始化程序并且您正在使用聚合初始化。参考资料提供了解释:

如果初始化器子句的数量小于成员数或初始化器列表完全为空,则剩余成员由其默认成员初始化器初始化(如果在类定义中提供),否则(C++14 起)由空列表初始化,按照通常的列表初始化规则(使用默认构造函数对非类类型和非聚合类执行值初始化,并为聚合执行聚合初始化)。

要对所有数据成员进行零初始化,请仅提供数据成员声明,​​不提供类内初始化程序,然后使用= {0}or={}语法:

struct data_t
{
    int num1;
    int num2;
    int num3;
    int num4;
};

int main()
{
    data_t d3 = { 0 };
    printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
        d3.num1, d3.num2, d3.num3, d3.num4);
}

现在,所有数据成员都初始化为0.

于 2020-04-30T20:54:53.953 回答