声明中的新C++ 11成员初始化功能是否使初始化列表过时了?

Vec*_*tor 68 c++ constructor initialization declaration c++11

使用C++ 11,我们现在可以在头声明中初始化类成员:

class aClass
{
    private:
        int mInt{100};
    public:
         aClass();
        ~aClass();
};
Run Code Online (Sandbox Code Playgroud)

所以我有点困惑.传统上,构造函数中的初始化列表已用于成员初始化:

aClass::aClass()
: mInt(100)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

声明中的新C++ 11成员初始化功能是否使初始化列表过时了?如果没有,一个优于另一个的优势是什么?什么情况会使声明初始化有利,或初始化列表有利?应该何时使用另一个?

Sha*_*our 71

不,它们不会过时,因为本文了解新的C++ 11初始化表单类成员初始化部分(强调我的)中说:

请记住,如果相同的数据成员在构造函数中同时具有类成员初始值设定项和mem-init,则后者优先.实际上,您可以通过为类成员初始值设定项的形式指定成员的默认值来利用此行为,如果构造函数没有该成员的显式mem-init,则将使用该行为.否则,构造函数的mem-init将生效,覆盖类成员初始值设定项.此技术在具有多个构造函数的类中很有用

因此,虽然在类成员初始化是一个很好的方便,它不会消除对初始化列表的需要,但两个功能相反,它们为您提供了一种指定默认值并在需要时覆盖它们的好方法.他说:这似乎也是Bjarne Stroustrup看到它的方式.

这节省了一些打字,但真正的好处来自具有多个构造函数的类.通常,所有构造函数都为成员使用公共初始值设定项:

并提供了一个具有公共初始化程序的成员示例:

class A {
  public:
    A(): a(7), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
    A(D d) : a(7), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
    int a, b;
  private:
    HashingFunction hash_algorithm;  // Cryptographic hash to be applied to all A instances
    std::string s;                   // String indicating state in object lifecycle
};
Run Code Online (Sandbox Code Playgroud)

并说:

hash_algorithm和s各自具有单个默认值的事实在代码混乱中丢失,并且在维护期间很容易成为问题.相反,我们可以分解数据成员的初始化:

class A {
  public:
    A(): a(7), b(5) {}
    A(int a_val) : a(a_val), b(5) {}
    A(D d) : a(7), b(g(d)) {}
    int a, b;
  private:
    HashingFunction hash_algorithm{"MD5"};  // Cryptographic hash to be applied to all A instances
    std::string s{"Constructor run"};       // String indicating state in object lifecycle
};
Run Code Online (Sandbox Code Playgroud)

注意:C++ 11中的缺点

在C++ 11中使用类成员初始化有一个缺点,因为它使类成为非聚合,我们不能再使用聚合初始化,这可能相当令人惊讶.在C++ 14中不是这种限制被删除的情况.有关更多详细信息,请参阅:具有非静态成员初始值设定项的类的C++ 11聚合初始化.

  • @MooingDuck 以及 `a` 和 `b` 在不同的构造函数中具有不同的值,因此在每种情况下都没有相同的默认值。这符合他的逻辑。 (2认同)
  • `a` 和 `b` 肯定有不同的值,但它们_do_在每种情况下都有相同的默认值。7->a 和 5->b。只是有时默认值会被覆盖。 (2认同)

qua*_*dev 7

不,他们没有过时.

如果您需要构造函数参数来初始化类成员,那么初始化列表仍然是唯一的方法.

class A
{
 int a=7; //fine, give a default value
public:
 A();
};

class B
{
 int b; 
public:
 B(int arg) : b(arg) {}

 B(int arg, bool b) : b(arg) { ... }
};
Run Code Online (Sandbox Code Playgroud)

请注意,如果两者都存在,构造函数的init将生效,覆盖类成员初始化,这对于指定类成员的默认值很有用.

  • @Vector 我不同意这里。为什么要浪费时间初始化一个数据成员(并且一个类类型的数据成员将*总是*被初始化)只是为了在之后立即分配给它?更不用说不可赋值的类型(例如`const`、引用),它们*必须*通过初始化来“设置”。mem-initialiser-list 用于初始化,这是构造函数应该做的。除非需要,否则不应分配。 (2认同)

Rei*_*ica 5

我看待它的方式,类内初始化是mem-initializer-lists的增强.在C++ 03中,未在mem-initializer-list中列出的成员始终默认初始化.这意味着类的默认构造函数,而不是基元类型的初始化.

类内初始化只允许您指定自己的默认值.有两种方法可以看待它.

一:如果您的类的大多数/所有构造函数想要为成员提供相同的初始值,请为该成员使用类内初始值设定项.对于其他成员,请使用mem-initializer-lists.当初始值取决于构造函数参数时,您当然必须使用它们.

另一个:为所有成员提供一个类内初始化程序,确切地说,类的默认构造函数将如何初始化它们.然后,非默认构造函数中的mem-initializer-lists获得"它与默认构造对象的区别"的语义.