0

通过值初始化的规则。值初始化发生:

1,5) 当使用由一对空括号或花括号组成的初始化程序创建无名临时对象时 (C++11 起);

2,6) 当具有动态存储持续时间的对象由 new 表达式创建时,初始化器由一对空括号或大括号组成 (C++11 起);

3,7) 当非静态数据成员或基类使用带有一对空括号或大括号的成员初始化程序进行初始化时 (C++11 起);

4) 当一个命名变量(自动的、静态的或线程局部的)用由一对大括号组成的初始化器声明时。

简单的例子

struct A{
    int i;
    string s;
    A(){};
};

A a{} 
cout << a.i << endl // default initialized value

没有显式声明的构造函数并留下默认的默认 ctor // 编译器生成我们得到的一个。

struct A{
    int i;
    string s;

};
A a{};
cout << a.i << endl // zero-initialized value

但是使用另一个结构。

struct A{
    int i;
    string s;

};

struct B{
    A a;
    int c;
};

B a{};
cout << a.a.i << endl // default initialized , even tho we did not , int struct B , declared A a{}.

ai 的值是零初始化的,即使没有使用 {} / () 构造,这违反了规则(如果我没记错的话)。

在结构 B 上使用相同的逻辑:

struct A{
    int i;
    string s;

};

struct B{
    A a;
    int c;
};

B b;
cout << b.c << endl; // default inicialized

我们根据规则获得行为。

最后一个例子:

struct A
{
    int i;
    A() { } 
};

struct B { A a; }; 

std::cout << B().a.i << endl;

B().ai 也是零初始化的,而我们显式声明了构造函数并且它没有被删除。

为什么这些值被零初始化?根据此处规定的规则,它们应该是默认初始化而不是零初始化。

感谢您的回答。

4

2 回答 2

2

A这是聚合与否的区别。

[dcl.init.aggr](强调我的)

聚合是一个数组或一个类(第 9 条),没有用户提供的构造函数(12.1),没有非静态数据成员的大括号或等号初始值设定项(9.2),没有私有或受保护的非静态数据成员(第9 条) 11),

所以当A没有声明的构造函数时,说A a{}聚合初始化的效果

它将用一个空的初始化列表构造每个成员:

如果列表中的初始化子句少于聚合中的成员,则每个未显式初始化的成员都应从空的初始化器列表中初始化

所以你得到了int{}std::string{}它将把整数成员值初始化为零。

当您确实提供了默认构造函数时,聚合属性将丢失并且int成员保持未初始化状态,因此访问它被视为未定义行为。


再具体一点:

此代码在访问时是未定义的行为,a.i因为您提供了用户定义的构造函数,因此该int i字段在构造后保持未初始化:

struct A{
    int i;
    string s;
    A(){};
};

A a{} ;
cout << a.i << endl;

并且此代码在访问时表现出未定义的行为,b.c因为您没有在以下位置执行列表初始化B b

struct B{
    A a;
    int c;
};

B b;
cout << b.c << endl;

所有其他代码都可以,并将对整数字段进行零初始化。在使用大括号的场景中{},您正在执行聚合初始化

最后一个示例有点棘手,因为您正在执行value-initialization。由于B是一个聚合,它被零初始化([dcl.init]),其中:

每个基类子对象都是零初始化的

因此,您再次可以访问A子对象的整数成员。

于 2017-11-03T13:44:15.550 回答
0

根据聚合初始化的规则,成员确实是值初始化的。

值初始化:

在所有情况下,如果使用空的大括号 {} 对并且 T 是聚合类型,则执行聚合初始化而不是值初始化。

聚合初始化的效果是:

  1. 如果初始化子句的数量少于成员和基数 (C++17 起) 或初始化器列表完全为空,则剩余的成员和基数 (C++17 起)由它们的默认初始化程序初始化,如果提供的话在类定义中,否则 (C++14 起) 为空列表,按照通常的列表初始化规则(对具有默认构造函数的非类类型和非聚合类执行值初始化,以及聚合初始化对于聚合)。如果引用类型的成员是这些剩余成员之一,则程序格式错误。
于 2017-11-03T13:54:49.887 回答