声明中新的 C + + 11成员初始化特性使得初始化列表过时了吗?

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

class aClass
{
private:
int mInt{100};
public:
aClass();
~aClass();
};

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

aClass::aClass()
: mInt(100)
{
...
}

声明中新的 C + + 11成员初始化特性使得初始化列表过时了吗?如果不是,那么这两者的优势是什么?什么情况会使声明时的初始化有利,或者使初始化列表有利?什么时候应该使用一个而不是另一个?

50674 次浏览

No, they are not obsolete.

Initialization lists are still the only way to go if you need a constructor's arguments to initialize your class members.

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) { ... }
};

Note that if both are present, the constructor's initialization will take effect, overriding the class member initialization, which is useful to specify a default value for a class member.

The way I look at it, in-class initialization is an ehancement of mem-initializer-lists. In C++03, members not listed in a mem-initializer-list were always default initialised. This means the default constructor for classes, and no initialization for primitive types.

In-class initialization simply allows you to specify your own defaults. There are two ways to look at it.

One: if most/all constructors of your class want to provide the same initial value for a member, use an in-class initializer for that member. For other members, use mem-initializer-lists. You'll of course have to use those whenever the initial value depends on constructor arguments.

The other one: provide an in-class initializer for all members, exactly how the default constructor of your class would initialise them. Then, mem-initializer-lists in non-default constructors get the semantics of "how it differs from a default-constructed object."

No, they are not obsolete as this article Get to Know the New C++11 Initialization Forms says in the Class Member Initialization section (emphasis mine):

Bear in mind that if the same data member has both a class member initializer and a mem-init in the constructor, the latter takes precedence. In fact, you can take advantage of this behavior by specifying a default value for a member in the form of a class member initializer that will be used if the constructor doesn't have an explicit mem-init for that member. Otherwise, the constructor's mem-init will take effect, overriding the class member initializer. This technique is useful in classes that have multiple constructors

So although in class member initialization is a nice convenience it does not remove the need for initialization lists but both features instead work together to give you a nice way to specify default values and override them when needed. This seems to be also how Bjarne Stroustrup sees it too, he says:

This saves a bit of typing, but the real benefits come in classes with multiple constructors. Often, all constructors use a common initializer for a member:

and provides an example of members which have a common initializer:

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
};

and says:

The fact that hash_algorithm and s each has a single default is lost in the mess of code and could easily become a problem during maintenance. Instead, we can factor out the initialization of the data members:

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
};

Note: disadvantage in C++11

There is one disadvantage to using in class member initialization in C++11 since it makes a class a non-aggregate we can no longer use aggregate initialization which may be rather surprising. This is not the case in C++14 where this restriction was removed. See: C++11 aggregate initialization for classes with non-static member initializers for more details.