我到处找了解这个问题的解释但是我很快就会出现问题。我在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)
答案 0 :(得分:6)
所有类型都包含内置类型的数据成员,因此除非您执行以下任一操作,否则它们都不会进行零初始化(以Foo
为例):
在默认构造函数中初始化成员:
class Foo {
public:
Foo() : a(), p() {}
int a,
*p;
};
或非静态数据成员初始值设定项( brace-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
编辑:我能够重现b
被零初始化的行为。如果您启用/sdl
(安全开发生命周期检查)编译器开关(在配置属性 - &gt; C / C ++ - &gt;常规下),则会发生这种情况。
来自开关的MSDN documentation:
启用
/sdl
时,编译器会生成代码以在运行时执行这些检查:
-...
- 执行类成员初始化。 在对象实例化时(构造函数运行之前)自动将所有类成员初始化为零。这有助于防止使用与构造函数未显式初始化的类成员关联的未初始化数据。
此blog post甚至提到了__autoclassinit
功能,尽管他列出的启发式方法与我们正在观察的内容完全匹配,因为此功能的行为has changed between VS2012和VS2013。
同样值得一提的是,编译器似乎不仅区分聚合(Foo
)和非聚合(其他两个),这是有道理的,但是,出于一些真正奇怪的原因,它只会如果在类定义中使用 class-key class
而不是struct
,则执行此零初始化。