当前位置: 首页 > 知识库问答 >
问题:

成员未归零,叮当响的错误?

龚铭
2023-03-14

考虑以下代码

class A {
public:
    int i;
    A() {}
};

class B {
public:
    A a;
    int i;
};

int main() {
    B* p = new B {};
    std::cout << p->i << " " << p->a.i << "\n";
}

编译时使用-std=c 11,在clang中,p-

编辑:由于评论中有一些广泛的讨论,我认为最好在这里添加一些标准摘录:

初始化类型为T的对象的值意味着:

  • 如果T是(可能符合cv条件的)类类型(第9条),具有用户提供的构造函数(12.1),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是病态的)
  • 如果T是一个(可能是cv限定的)非并集类类型,没有用户提供的构造函数,则对象初始化为零,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数
  • 如果T是数组类型,则每个元素都初始化为值
  • 否则,对象初始化为零

将T类型的对象或引用初始化为零意味着:

  • 如果T是标量类型(3.9),则将对象设置为值0,作为积分常数表达式,转换为T

每个项目的第二个项目符号适用于此处。


共有3个答案

东郭海阳
2023-03-14

它是 <罢工> 不 编译错误 <罢工> ,这是您代码中的一个错误 。编译器似乎实现了C 03行为,但在C 11中这一点发生了至关重要的变化。

以下是C 03和C 11标准中的一些相关引用

在C 03中:

对T类型的对象进行值初始化意味着:

-如果T是具有用户声明构造函数(12.1)的类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是病态的);

-如果T是没有用户声明构造函数的非并集类类型,则T的每个非静态数据成员和基类组件都是值初始化的;

(强调地雷)

在C 11中:

对T类型的对象进行值初始化意味着:

-如果T是一个(可能是cv限定的)类类型(第9条),具有用户提供的构造函数(12.1),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是病态的);

-如果T是一个(可能是cv限定的)非联合类类型,没有用户提供的构造函数,则对象初始化为零,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数。

将T类型的对象或引用初始化为零意味着:

-如果T是标量类型(3.9),则将对象设置为值0(零),作为积分常数表达式,转换为T;

  • 如果T是(可能是cv限定的)非并集类类型,则每个非静态数据成员和每个基类子对象初始化为零,填充初始化为零位

注:以下仅适用于C 03:

删除用户提供的构造函数,或将其更改为

A() : i() {}

当您在此处初始化aB时,

B* p = new B {};

它值初始化其数据成员。由于具有默认构造函数,因此值初始化会导致调用该构造函数。但是该构造函数没有显式初始化A::i,因此它得到默认初始化,这对于int意味着不执行初始化。

如果您没有为a提供默认构造函数,那么当a值初始化时,数据成员将初始化为零。

司徒高寒
2023-03-14

它确实看起来像一个bug(或者,正如评论中指出的,尽管指定了C 11,但仍按照C 03进行操作)。在C 11中,值初始化应该在调用其默认构造函数之前将a的成员归零。B的初始化受8.5/7规则管辖

如果T是没有用户提供的构造函数的(可能是cv限定的)非联合类类型,则对象是零初始化的,并且如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数。

零初始化应根据8.5/5的规则递归地对a进行零初始化

如果T是(可能是cv限定的)非并集类类型,则每个非静态数据成员和每个基类子对象都初始化为零

当然,a的零初始化应该设置为零。

庞彬
2023-03-14

根据C 11标准和相关DRs,Clang是正确的

在最初的C 11规范中,B{}将执行值初始化,导致a.i被零初始化。与C 98相比,在以下情况下,这是一种行为变化:

B b = {};

... 在C 98中作为聚合初始化处理,但在C 11 FDI中作为值初始化处理。

但是,这种情况下的行为被核心问题1301更改,该问题通过强制在括号初始化聚合时使用聚合初始化来恢复C 98行为。由于此问题被视为DR,因此它被视为事实上适用于C标准的早期修订,因此符合要求的C 11编译器将在此处执行聚合初始化而不是值初始化。

归根结底,依赖值初始化来初始化数据成员是个坏主意,尤其是对于具有用户提供的构造函数的类。

 类似资料:
  • 数据分发四步走 1.下载模板 ● 下载带员工信息的模板 可下载模板直接使用,也可下载后自己修改 模板中userID匹配钉钉上对应的员工,匹配错误数据会发错 总计之后的列名可以随意修改 2.上传数据 ● 下载带员工信息的模板 注意:IE浏览器无法弹出文件浏览框,可切换其他浏览器 3.检查数据 ● 上传前数据检查,避免问题 实际结果展示,如有问题,会有错误提示 4.发送数据 ● 手机端效果核对 自定义

  • 我试图推出新的STL库,但由于某些原因,出现了错误。网站表示,它应该支持新的library–我相信 我使用了另一个Stack Exchange帖子中的一些代码,因此它应该根据赞成票的数量有效。这应该转到指定的目录并打印该目录中的所有文件。这是代码。 我收到的错误消息是: 我确保包含头,而不仅仅是删除了Cleion中的任何红色波浪线。我尝试从CLion以及命令行进行编译。我使用的编译字符串是: 有没

  • 错误:FirebaseAppPlatform.verifyExtends(_delegate);

  • 我成功地签出了llvm(v12)项目,并构建了llvm-cang调用: 执行此操作后,出现空的<code>clang tools</code>目录。调用<code>make clang</code>后,它仍然为空。当我调用<code>make clang tools</code>时,它什么也不做——有一个目标使用这个名称,因为没有关于丢失目标的错误信息,但该命令既不执行也不打印任何内容。另一方面,

  • 这是我的ProdottoController: Prodotto实体: 这是视图(Freemarker): 当我尝试将值更新到表中时,出现以下错误: FreeMarker模板错误(调试模式;在生产中使用RETHROW!):以下内容已评估为null或缺失:==

  • 问题内容: 在Java的什么情况下,显式null有用。它是否通过使对象不可访问或其他方式以任何方式辅助垃圾收集器?是否认为这是一种好习惯? 问题答案: 在Java中,如果您有一个运行时间很长的方法,并且对对象的唯一引用是通过局部变量,则 可以提供 帮助。在不再需要该局部变量时(但当该方法将继续运行很长时间时)将该局部变量设置为null 可以 帮助GC。(在C#中,这很少有用,因为GC考虑了“最后可