class A { public: int x[100]; };

声明A a不会初始化对象(通过x字段中的垃圾值可以看到)。
以下将触发初始化:A a{}auto a = A()auto a = A{}

这三个中的哪一个应该更受青睐吗?

接下来,让我们使其成为另一个类的成员:
class B { public: A a; };
B的默认构造函数似乎负责a的初始化。
但是,如果使用自定义构造函数,则必须照顾好它。
以下两个选项起作用:
class B { public: A a;  B() : a() { } };

或者:
class B { public: A a{};  B() { } };

是否应该选择两者中的任何一个?

最佳答案

初始化



正确的A a是在没有初始化程序的情况下定义的,并且不满足default initialization的任何要求。



是的;

  • a{}执行list initialization,其中
  • 如果{}为空,则
  • 变为value initialization;如果A是聚合的,则可能为aggregate initialization
  • 即使删除了默认构造函数也可以使用。例如A() = delete;(如果仍然将“A”视为集合)
  • 将警告缩小转换范围。




  • 是的;
  • 这是copy initialization,其中的prvalue临时项是使用direct initialization ()构建的,

    如果()为空,则
  • 使用value initialization
  • 没有aggregate initialization的希望。
  • 然后将prvalue临时值用于direct-initialize对象。
  • Copy elision通常可以用来优化副本并就地构建A
  • 允许跳过复制/移动构造函数的副作用。
  • 不能删除Move构造函数。例如A(A&&) = delete;
  • 如果删除了复制构造函数,那么必须存在move构造函数。例如A(const A&) = delete; A(A&&) = default;
  • 将不会警告缩小转换范围。




  • 是的;
  • 这是copy initialization,其中的prvalue临时项是使用list initialization {}构建的,

    如果{}为空,则
  • 使用value initialization;如果A是聚合,则
  • 可以使用聚合初始化。
  • 然后将prvalue临时值用于direct-initialize对象。
  • Copy elision通常可以用来优化副本并就地构建A
  • 允许跳过复制/移动构造函数的副作用。
  • 不能删除Move构造函数。例如A(A&&) = delete;
  • 如果删除了复制构造函数,那么必须存在move构造函数。例如A(const A&) = delete; A(A&&) = default;
  • 将警告缩小转换范围。
  • 即使删除了默认构造函数也可以使用。例如A() = delete;(如果仍然将“A”视为集合)



  • 显然,您应该更喜欢A a{}

    成员初始化



    不,这是不正确的。
  • “B”的隐式定义的默认构造函数将调用A的默认构造函数,但不会初始化成员。没有直接或列表初始化将被触发。此示例的语句B b;将调用默认构造函数,但保留A数组的不确定值。



  • 这将起作用;
  • : a()constructor initializer,而a()是成员初始化程序,是member initializer list的一部分。
  • 使用direct initialization ();如果()为空,则使用value initialization
  • 没有希望使用聚合初始化。
  • 将不会警告缩小转换范围。



  • 这将起作用;
  • a现在有一个non-static data member initializer,如果您使用聚合初始化并且编译器为not fully C++14 compliant,则可能需要构造函数对其进行初始化。
  • 成员初始化程序使用list initialization {},其中
  • 如果{}为空,则
  • 可能会变成value initialization,如果A是一个聚合,则aggregate initialization可能会变成some exceptions
  • 如果a是唯一成员,则不必定义默认构造函数,并且将隐式定义默认构造函数。

  • 显然,您应该更喜欢第二种选择。

    就个人而言,我更喜欢在任何地方使用花括号,member initializer lists用作auto,以及构造函数可能将其误认为std::initializer_list的情况:
    class B { public: A a{}; };
    
    std::vector构造函数对于std::vector<int> v1(5,10)std::vector<int> v1{5,10}的行为将有所不同。使用(5,10),您将获得5个元素,每个元素的值均为10;但是使用{5,10},您将获得两个元素,分别包含5和10,因为如果使用花括号,则std::initializer_list是首选。这在Scott Meyers的《 Effective Modern C++》的第7条中有很好的解释。

    特别是对于Direct initialization,可以考虑两种格式:
  • value initialization a(),如果()为空,则变为List initialization
  • value initialization a{},如果{}为空,它也将变成list initialization

  • 幸运的是,在成员初始化器列表中,没有最烦人的解析风险。在初始化程序列表之外,作为自己的一条语句,A a()应该声明了一个函数,而A a{}已经清楚了。另外,ojit_a的好处是可以防止转换变窄。

    因此,总而言之,此问题的答案是它取决于您要确定的内容,并且将确定您选择的形式。对于空的初始值设定项,规则更为宽容。

    关于c++11 - 类成员初始化的首选方式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36270612/

    10-09 08:20