【问题标题】:memset() to initialize object in constructor?memset() 在构造函数中初始化对象?
【发布时间】:2015-03-02 11:03:11
【问题描述】:

我发现了这段使用 memset() 初始化对象的 C++ 代码:

struct Message
{
   Message()
   {
      memset(this, 0, sizeof(Message));
   }

   unsigned int a, b, c;
};

由于这是一个POD结构,这段代码应该没问题。
使用 memset 代替构造函数有什么好处,例如:

Message() : a(0), b(0), c(0) {}

【问题讨论】:

  • 考虑如果你的对象有虚拟表指针会发生什么。好的编译器很可能会确定您有 POD 和 memset 它们或在第二种情况下类似的东西。但是如果稍后决定建立一个层次结构,那你就搞砸了。
  • 至少在 memset 之前/之后插入一个static_assert。这告诉人类读者和编译器你假设 Message 是一个 POD;如果不是这样,编译器就会失败,读者就会知道为什么你认为 memset 没问题。
  • @leemes:类似于static_assert(std::is_pod<Message>::value);。但是对于一个重要的默认构造函数,它永远不会是一个 POD,不是吗?也许它应该检查标准布局类型?
  • 我非常怀疑,memset 应该强制调用内存,它不会被优化,因为它会被认为有副作用。静态声明初始值为编译器提供了更多优化选项。
  • @leemes:那么我会选择:static_assert(std::is_standard_layout<Message::value, "class is not memsettable")

标签: c++ constructor initialization memset


【解决方案1】:

像这样使用memset() 没有任何优势。抛开所有明显的缺点和未来的痛苦,有一个缺点使它的效率低于

Message() : a(0), b(0), c(0) {}

这是因为PODs 通常存储在数组中。如此好的(智能)编译器将有一个优势,即用单个 memset() 替换数组中多个对象的初始化,以防出现

Message * messages_1 = new Message[100];

或者

std::vector<Message> messages_2;
messages_2.resize(100);

即使只构造单个对象,好的编译器也会在幕后使用memset()

【讨论】:

  • memset 方法确实有一个优势,即在 填充字节 中具有确定性的 0 值。有时这很重要,也许对于将其作为数据块而不是对象读取的哈希函数而言。
【解决方案2】:

请注意,在 C++11 和更新版本中,您有比其中任何一个更好的选择:

struct Message
{
   unsigned int a = 0;
   unsigned int b = 0;
   unsigned int c = 0;
};

这应该产生与带有初始化列表的构造函数方法相同的代码(和优化机会),同时:

  • 使用更少的代码。
  • 更具可读性。
  • 无需担心在添加成员时更新初始化列表。
  • 无需考虑如果以后b 需要默认为-1 该怎么办。

【讨论】:

    猜你喜欢
    • 2021-11-06
    • 2021-09-10
    • 1970-01-01
    • 2012-03-05
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多