在 C++ 代码库中替换 类 上的 memset()
Replacing memset() on classes in a C++ codebase
我继承了一个 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 构建时会导致相当莫名其妙的结果(本质上是垃圾值),这意味着我对初始化列表的理解在某种程度上是缺乏的。
所以,问题:
- 为什么这不起作用?
- 有什么更好的方法来替换 memset 的这种使用,以确保具有构造函数的成员被正确地默认初始化,并且最好可以可靠地应用尽可能少的搜索和替换(不幸的是没有测试) .如果它适用于 VS2013 之前的版本,则加分。
我认为布置 new
已经足够成熟,可以在 VS 上运行,因此您可以尝试:
#include <new>
new(&o) T();
new(something->p) T();
确保不要对任何尚未分配的对象执行此操作,destructed/uninitialized 首先! (但下面指出,如果构造函数抛出异常,这可能会失败。)
您也许可以从默认对象进行赋值,即 o = T();
或 *(something->p) = T();
。一个好的通用策略可能是为每个 POD 类 提供一个简单的默认构造函数,在初始化列表中包含 : o()
。
大括号初始化的行为取决于您尝试初始化的对象类型。
在聚合(例如简单的 C 风格结构)上使用空的大括号初始化器对聚合进行零初始化,即它使所有成员都为零。
在非聚合上,一个空的大括号初始化器调用默认构造函数。如果构造函数没有显式初始化成员(编译器自动生成的构造函数没有),那么成员将被构造但未初始化。具有自己初始化自己的构造函数的成员是可以的,但是例如int
成员的值不确定。
解决问题的最佳方法,IMO,是添加一个默认构造函数(如果 类 还没有)和一个显式初始化成员的初始化列表。
It works on *nix systems, but results in fairly inexplicable results (in essence, garbage values) when built with VS2013 with v120_xp toolset, which implies that my understanding of initializer lists is somehow lacking.
'default' 初始化的规则在不同版本的 C++ 中发生了变化,但是 VC++ 一直坚持 C++98 规则,甚至忽略了 C++03 的更新我觉得。
其他编译器已经实施了新规则,gcc 甚至在某一时刻实施了一些未被接受的缺陷解决方案,以便将来包含在官方规范中。
因此,即使您想要的 是 由标准保证的,在大多数情况下,最好不要尝试依赖不符合标准的成员的初始化行为有明确的初始值设定项。
我继承了一个 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 构建时会导致相当莫名其妙的结果(本质上是垃圾值),这意味着我对初始化列表的理解在某种程度上是缺乏的。
所以,问题:
- 为什么这不起作用?
- 有什么更好的方法来替换 memset 的这种使用,以确保具有构造函数的成员被正确地默认初始化,并且最好可以可靠地应用尽可能少的搜索和替换(不幸的是没有测试) .如果它适用于 VS2013 之前的版本,则加分。
我认为布置 new
已经足够成熟,可以在 VS 上运行,因此您可以尝试:
#include <new>
new(&o) T();
new(something->p) T();
确保不要对任何尚未分配的对象执行此操作,destructed/uninitialized 首先! (但下面指出,如果构造函数抛出异常,这可能会失败。)
您也许可以从默认对象进行赋值,即 o = T();
或 *(something->p) = T();
。一个好的通用策略可能是为每个 POD 类 提供一个简单的默认构造函数,在初始化列表中包含 : o()
。
大括号初始化的行为取决于您尝试初始化的对象类型。
在聚合(例如简单的 C 风格结构)上使用空的大括号初始化器对聚合进行零初始化,即它使所有成员都为零。
在非聚合上,一个空的大括号初始化器调用默认构造函数。如果构造函数没有显式初始化成员(编译器自动生成的构造函数没有),那么成员将被构造但未初始化。具有自己初始化自己的构造函数的成员是可以的,但是例如int
成员的值不确定。
解决问题的最佳方法,IMO,是添加一个默认构造函数(如果 类 还没有)和一个显式初始化成员的初始化列表。
It works on *nix systems, but results in fairly inexplicable results (in essence, garbage values) when built with VS2013 with v120_xp toolset, which implies that my understanding of initializer lists is somehow lacking.
'default' 初始化的规则在不同版本的 C++ 中发生了变化,但是 VC++ 一直坚持 C++98 规则,甚至忽略了 C++03 的更新我觉得。
其他编译器已经实施了新规则,gcc 甚至在某一时刻实施了一些未被接受的缺陷解决方案,以便将来包含在官方规范中。
因此,即使您想要的 是 由标准保证的,在大多数情况下,最好不要尝试依赖不符合标准的成员的初始化行为有明确的初始值设定项。