【问题标题】:Constructor to specify zero-initialization of all builtin members?构造函数指定所有内置成员的零初始化?
【发布时间】:2014-04-09 22:35:56
【问题描述】:

类的构造函数是否有更简单的方法来指定内置类型的所有成员都应为零初始化?

这段代码 sn-p 出现在另一个帖子中:

struct Money
{
    double amountP, amountG, totalChange;
    int twenty, ten, five, one, change;
    int quarter, dime, nickel, penny;

    void foo();
    Money()  {}
};

原来问题是对象是通过Money mc;实例化的,变量没有初始化。

推荐的解决方案是添加以下构造函数:

Money::Money()
  : amountP(), amountG(), totalChange(),
    twenty(), ten(), five(), one(), change()
    quarter(), dime(), nickel(), penny()
{
}

但是,这很丑陋且不便于维护。添加另一个成员变量并忘记将其添加到构造函数中的长列表很容易,当未初始化的变量偶然突然停止获取0 时,可能会导致几个月后难以发现的错误。

【问题讨论】:

  • @chris 如果没有用户定义的构造函数,这是个好主意,但如果该类还有一些其他要在构造时运行的代码,则可能会很尴尬
  • 抱歉,直到刚才我才意识到这有多糟糕。

标签: c++ constructor initialization built-in-types


【解决方案1】:

您可以使用子对象进行整体初始化。会员可以工作,但您需要获得所有访问权限。所以继承更好:

struct MoneyData
{
    double amountP, amountG, totalChange;
    int twenty, ten, five, one, change;
    int quarter, dime, nickel, penny;
};

struct Money : MoneyData
{
    void foo();
    Money() : MoneyData() {} /* value initialize the base subobject */
};

演示(放置new用于确保对象创建前内存非零):http://ideone.com/P1nxN6

对比问题中的代码略有不同:http://ideone.com/n4lOdj

在上述两个演示中,double 成员被删除以避免可能的无效/NaN 编码。

【讨论】:

  • 不错的主意,考虑所有因素
  • @M.M:不幸的是,我并不真正相信演示,因为它也为问题中的代码打印零。 g++ 必须在放置 new 期间归零。
  • 警告:在 C++98 中,如果 MoneyData 还包含一个非 POD 成员(例如 std::string),那么突然 intdouble 成员不是零初始化的更多。
  • @Destructor 在 C++98 中,您的代码通过读取未初始化的变量导致未定义的行为,因此任何输出都将符合标准。相关条款是 8.5/7 “初始化程序为 () 的对象,应默认初始化。”,8.5/5 定义 default-initialized “如果 T 是非 POD 类类型,T 的默认构造函数称为 ",并且 12.1/7 表示隐式声明的默认构造函数的行为就像它有一个空的函数体。 12.6/4 表示非类类型的非静态数据成员,没有初始化器,不会被构造函数初始化。
  • 在 C++03 中它被更改为 () 递归地对聚合进行值初始化,无论它是否是 POD
【解决方案2】:

对于在 C++98 中工作的东西,您可以使用 Boost 的 value_initialized: (live example)

#include <boost/utility/value_init.hpp>
...
struct Money
{
    boost::value_initialized<double> amountP, amountG, totalChange;
    boost::value_initialized<int> twenty, ten, five, one, change;
    boost::value_initialized<int> quarter, dime, nickel, penny;

    void foo();
    Money()  {/*every member is 0*/}
};

【讨论】:

  • 好答案 .. 我接受 Ben 的,因为它可以在没有 boost 的情况下完成,但你的也很可靠
  • 绝对 (+) 很好地用于模板,不使用 boost 也可以实现简单的实现。
【解决方案3】:

您可以通过以下方式使用默认构造函数

Money m = {};

或者您可以在类定义中设置数据成员的初始化:

struct Money
{
    double amountP = 0.0, amountG = 0.0, totalChange = 0.0;
    int twenty = 0, ten = 0, five - 0, one = 0, change = 0;
    int quarter = 0, dime = 0, nickel = 0, penny = 0;

    void foo();
    Money()  {}
};

【讨论】:

  • 我不想依赖调用者做= {};,我宁愿我的对象正常工作,即使他们写Money m; m.foo();。第二个选项很好,但只能在 C++11 中使用,如果也有 C++98 解决方案,那就太好了。
  • 您的第一个解决方案不起作用,因为 OP 定义了 NOP 默认构造函数。他需要要么摆脱它,要么 default 它而不是空虚的身体。
  • @VladfromMoscow,由于有用户提供的构造函数,它不是聚合。因此,列表初始化值初始化对象。由于存在用户提供的默认构造函数,因此它被默认初始化。初始化器是 8.5,聚合是 8.5.1,列表初始化是 8.5.4。
  • @VladfromMoscow,转到 8.5 到值初始化的第一个点。 对 T 类型的对象进行值初始化意味着: — 如果 T 是(可能是 cv 限定的)类类型(第 9 条),没有默认构造函数(12.1)或 默认构造函数是用户-提供或删除,则对象默认初始化;
  • @VladfromMoscow:具有用户声明构造函数的对象不能零初始化,因为这可能会破坏构造函数应该建立的不变量。 (如果它具有静态存储持续时间,它可能仍会预初始化为零,但对象的生命周期在用户构造函数完成执行之前不会开始。没有办法绕过用户构造函数并改为初始化为零。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 1970-01-01
  • 2021-09-04
  • 1970-01-01
相关资源
最近更新 更多