如何确保您的对象是零初始化的?

How to make sure your object is zero-initialized?

更新:我正在寻找是否有一种方法可以立即对整个 class 进行零初始化,因为从技术上讲,人们可能会忘记添加 ' = 0' 或 '{}' 在每个成员之后。其中一条评论提到,显式默认的无参数 c-tor 将 启用 MyClass c{}; 形式的值初始化期间进行零初始化。查看 http://en.cppreference.com/w/cpp/language/value_initialization 我无法弄清楚哪个语句指定了这一点。

初始化现在是一个复杂的话题,因为 C++11 已经改变了各种初始化结构的含义和语法。我无法从其他问题中收集到足够好的信息。但是,例如 Writing a Default Constructor Forces Zero-Initialization?.

我面临的具体问题是:我想确保我的 classes 的成员在声明默认 c-tor 的 (1) classes 中都被清零,对于 (2) 那些没有的。

对于 (2),使用 {} 进行初始化就可以完成这项工作,因为它是值初始化的语法,转换为零初始化,或者如果您的 class 是聚合,则转换为聚合初始化- 没有提供初始化程序的成员(全部!)被零初始化的情况。

但是对于 (1),我仍然不确定最好的方法是什么。从我收集到的所有信息中,我了解到 如果您提供默认的 c-tor(例如,将某些成员设置为某些值),您必须明确地将剩余成员归零,否则语法 MyClass c = MyClass(); 或C++11 MyClass c{}; 不会做这项工作。换句话说,在这种情况下,值初始化意味着只调用你的 c-tor,就是这样(没有归零)。

如果您声明一个接受值的 c-tor,并将这些值设置为成员的子集,但您希望其他成员为零,则您 运行 会遇到相同的情况:没有 shorthand 可以这样做 - 我正在考虑 3 个选项:

class MyClass
{
  int a;
  int b;
  int c;

  MyClass(int a)
  {
    this->a = a;
    // now b and c have indeterminate values, what to do? (before setting 'a')


    // option #1
    *this = MyClass{}; // we lost the ability to do this since it requires default c-tor which is inhibited by declaring this c-tor; even if we declare one (private), it needs to explicitly zero members one-by-one

    // option #2
    std::memset(this, 0, sizeof(*this)); // ugly C call, only works for PODs (which require, among other things, a default c-tor defaulted on first declaration)

    // option #3
    // don't declare this c-tor, but instead use the "named constructor idiom"/factory below
  }


  static MyClass create(int a)
  {
    MyClass obj{}; // will zero-initialize since there are no c-tors
    obj.a = a;
    return obj;
  }
};

我的推理是否正确? 会选择 3 个选项中的哪一个?

使用 in-class 初始化怎么样?

class Foo
{
    int _a{}; // zero-it
    int _b{}; // zero-it
public:
    Foo(int a): _a(a){} // over-rules the default in-class initialization
};

选项 4 和 5:

选项 4:


MyClass(int a) :a(a), b(0), c(0)
  {
  }

选项 5:


class MyClass
  {
      int a = 0;
      int b = 0;
      int c = 0;

      MyClass(int a) : a(a) {
      }
  }

以我拙见,确保零初始化的最简单方法是添加一个抽象层:

class MyClass
{
    struct
    {
        int a;
        int b;
        int c;
    } data{};
public:
    MyClass(int a) : data{a} {}
};

将数据成员移动到结构中让我们可以使用值初始化来执行零初始化。当然,现在访问这些数据成员有点麻烦:data.a 而不是 MyClass 中的 a。 由于 data 的大括号初始化器,MyClass 的默认构造函数将执行 data 及其所有成员的零初始化。此外,我们可以在 MyClass 的构造函数中使用聚合初始化,它也可以对那些未显式初始化的数据成员进行值初始化。

间接访问数据成员的缺点可以通过使用继承而不是聚合来克服:

struct my_data
{
    int a;
    int b;
    int c;
};

class MyClass : private my_data
{
    MyClass() : my_data() {}
public:
    MyClass(int a) : MyClass() { this->a = a; }
};

通过显式指定基初始化器 my_data(),值初始化也会被调用,从而导致零初始化。此默认构造函数可能应标记为 constexprnoexcept。请注意,它不再是微不足道的。我们可以使用聚合初始化或转发构造函数来使用初始化而不是赋值:

class MyClass : private my_data
{
public:
    MyClass(int a) : my_data{a} {}
};

您还可以编写一个包装器模板来确保零初始化,认为在这种情况下好处是有争议的:

template<typename T>
struct zero_init_helper : public T
{
    zero_init_helper() : T() {}
};

struct my_data
{
    int a;
    int b;
    int c;
};

class MyClass : private zero_init_helper<my_data>
{
public:
    MyClass(int a) { this->a = a; }
};

有了用户提供的构造函数,zero_init_helper 不再是聚合,因此我们不能再使用聚合初始化。要在 MyClass 的 ctor 中使用初始化而不是赋值,我们必须添加一个转发构造函数:

template<typename T>
struct zero_init_helper : public T
{
    zero_init_helper() : T() {}

    template<typename... Args>
    zero_init_helper(Args&&... args) : T{std::forward<Args>(args)...} {}
};

class MyClass : private zero_init_helper<my_data>
{
public:
    MyClass(int a) : zero_init_helper(a) {}
};

约束构造函数模板需要一些 is_brace_constructible 特征,这不是当前 C++ 标准的一部分。但这已经是一个非常复杂的问题解决方案。


也可以按如下方式实施您的选项 #1:

class MyClass
{
  int a;
  int b;
  int c;

  MyClass() = default; // or public, if you like

public:
  MyClass(int a)
  {
    *this = MyClass{}; // the explicitly defaulted default ctor
                       // makes value-init use zero-init
    this->a = a;
  }
};

构造函数委托呢?

class MyClass
{
  int a;
  int b;
  int c;

  MyClass() = default; // or public, if you like

public:
  MyClass(int a) : MyClass() // ctor delegation
  {
      this->a = a;
  }
};

[class.base.init]/7 建议上述示例应调用值初始化,这会导致零初始化,因为 class 没有任何用户 - 提供 默认构造函数 [dcl.init]/8.2。最近版本的 clang++ 似乎对对象进行了零初始化,而最近版本的 g++ 则没有。我已将此报告为 g++ bug #65816