c++ 11 VS2013类POD成员初始化

C++11 VS2013 class POD member initialization

本文关键字:成员 初始化 POD VS2013 c++      更新时间:2023-10-16

我已经到处寻找一个解释,但我短。我从VS2013 v120平台工具集看到这种行为,但是当我将工具集设置为v90 (VS2008工具集)时,一切都未初始化。我相信这是由于c++ 11中的一些变化,但它也可能是v120平台工具集的异常。

谁能解释一下c++/c++ 11级别发生了什么?也就是说,为什么b是零?为什么j不等于零?(即为什么结构体的行为不同于类的行为)

另外,我知道我输出数据的方式是未定义的行为,请忽略它。在这里以这种方式发布比在调试器窗口发布更容易。这是在32位上运行的,因此指针的大小与unsigned int相同。

考虑以下代码:

#include <iostream>
class Foo {
public:
  int a,
      *p;
};
class Bar {
public:
  Bar(){}
  int a,
      *p;
};
struct Jar {
  Jar(){}
  int a,
      *p;
};
int main() {
  Foo f;
  Bar b;
  Jar j;
  std::cout << std::hex; // please excuse this undefined-behavior producing test code, it's more simple to show this than a debugger window on SO (this is on 32-bit)
  std::cout << "f: " << ((unsigned*)&f)[0] << ' ' << ((unsigned*)&f)[1] << std::endl;
  std::cout << "b: " << ((unsigned*)&b)[0] << ' ' << ((unsigned*)&b)[1] << std::endl;
  std::cout << "j: " << ((unsigned*)&j)[0] << ' ' << ((unsigned*)&j)[1] << std::endl;
  return 0;
}

输出:

f: cccccccc cccccccc  
b: 0 0  
j: cccccccc cccccccc
编辑:


这是我看到的与Bar b;相关的反汇编。__autoclassinit2将内存归零。它不是构造函数的一部分,而是在调用构造函数之前归零。

  Bar b;
00886598  push        8  
0088659A  lea         ecx,[b]  
0088659D  call        Bar::__autoclassinit2 (0881181h)  
008865A2  lea         ecx,[b]  
008865A5  call        Bar::Bar (0881109h)  

所有类型都包含内置类型的数据成员,因此它们都不会被零初始化,除非您执行以下操作之一(以Foo为例):

初始化默认构造函数中的成员:

class Foo {
public:
  Foo() : a(), p() {}
  int a,
      *p;
};

或非静态数据成员初始化项(大括号-or-equal-initializer)

class Foo {
public:
  int a = 0,
      *p = nullptr;
};

或保持Foo不变,并初始化实例

Foo f{};

使用原始示例,我无法重现使用VS2013, 32位调试构建观察到的结果。得到的输出是

f: cccccccc cccccccc
b: cccccccc cccccccc
j: cccccccc cccccccc

EDIT:我能够重现b被零初始化的行为。如果您启用/sdl (安全开发生命周期检查)编译器开关(在配置属性-> C/c++ -> General下),就会发生这种情况。

来自交换机的MSDN文档:

当启用/sdl时,编译器生成在运行时执行这些检查的代码:
本;...
本;执行类成员初始化。在对象实例化时自动将所有类成员初始化为零(在构造函数运行之前)。这有助于防止使用与构造函数未显式初始化的类成员关联的未初始化数据。

这篇博文甚至提到了__autoclassinit函数,尽管他列出的启发式方法与我们观察到的并不完全匹配,因为该功能的行为在VS2012和VS2013之间发生了变化。

同样毫无价值的是,编译器似乎不仅区分聚合(Foo)和非聚合(其他两个),这有一定的意义,但是,由于一些真正奇怪的原因,它只会执行这种零初始化,如果您使用类键 class,而不是struct,在类定义中。