如何确保您的对象是零初始化的?
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()
,值初始化也会被调用,从而导致零初始化。此默认构造函数可能应标记为 constexpr
和 noexcept
。请注意,它不再是微不足道的。我们可以使用聚合初始化或转发构造函数来使用初始化而不是赋值:
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。
更新:我正在寻找是否有一种方法可以立即对整个 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()
,值初始化也会被调用,从而导致零初始化。此默认构造函数可能应标记为 constexpr
和 noexcept
。请注意,它不再是微不足道的。我们可以使用聚合初始化或转发构造函数来使用初始化而不是赋值:
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。