我继承了C ++ 98代码库,它在C ++类上有两个主要的memset()用途,为了清晰起见扩展了宏:
// pattern #1:
Obj o;
memset(&o, 0, sizeof(o));
// pattern #2:
// (elsewhere: Obj *o;)
memset(something->o, 0, sizeof(*something->o));
您可能已经猜到,此代码库不使用STL或其他非POD类。当我尝试将std::string
放到其中一个类中时,通常会发生不好的事情。
据我了解,这些模式可以在C ++ 11中重写如下:
// pattern #1
Obj o = {};
// pattern #2
something->o = {};
也就是说,{}的赋值会在两种情况下用默认初始值重写对象的内容。很好,很干净,不是吗?
嗯,是的,但它没有用。它适用于* nix系统,但是当使用带有v120_xp工具集的VS2013构建时会产生相当难以理解的结果(实质上是垃圾值),这意味着我对初始化程序列表的理解在某种程度上是缺乏的。
所以,问题:
答案 0 :(得分:2)
大括号初始化的行为取决于您尝试初始化的对象类型。
在聚合(例如简单的C风格结构)上使用空的大括号初始化器零初始化聚合,即它使所有成员为零。
在非聚合上,空的brace-initializer调用默认构造函数。如果构造函数没有显式初始化成员(编译器自动生成的构造函数没有),那么将构造成员但是未初始化。拥有自己初始化自己的构造函数的成员也可以,但是int
成员将具有不确定的价值。
解决问题的最佳方法IMO是添加一个默认构造函数(如果类已经没有它),并使用初始化列表明确初始化成员。
答案 1 :(得分:1)
它适用于* nix系统,但是当使用带有v120_xp工具集的VS2013构建时,会产生相当难以理解的结果(实质上是垃圾值),这意味着我对初始化程序列表的理解在某种程度上是缺乏的。
“默认”初始化的规则已经从C ++的版本更改为版本,但是VC ++坚持使用C ++ 98规则,甚至忽略了C ++ 03中的更新。
其他编译器已经实施了新的规则,gcc在某一点上甚至实现了一些未被接受的官方规范中未被接受的缺陷解决方案。
因此,即使您希望 标准保证,但在大多数情况下,最好不要尝试依赖没有显式初始化器的成员的初始化行为。< / p>
答案 2 :(得分:-1)
我认为展示位置new
足以使其适用于VS,因此您可以尝试:
#include <new>
new(&o) T();
new(something->p) T();
请确保不要在任何尚未分配和破坏/未初始化的对象上执行此操作! (但是下面指出,如果构造函数抛出异常,这可能会失败。)
您可以只从默认对象分配,即o = T();
或*(something->p) = T();
。一个好的通用策略可能是在初始化列表中为每个POD类提供一个带有: o()
的普通默认构造函数。