Has the new C++11 member initialization feature at declaration made initialization lists obsolete?
Asked Answered
B

3

93

With C++11, we now have the ability to initialize class members in a header declaration:

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

So I'm a bit confused. Traditionally initialization lists in constructors have been used for member initialization:

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

Has the new C++11 member initialization feature at declaration made initialization lists obsolete? If not, what are the advantages of one over the other? What situations would make initialization at declaration advantageous, or initialization lists advantageous? When should one be used over the other?

Baruch answered 10/6, 2014 at 20:4 Comment(5)
Obviously not. Consider constructor arguments.Emmittemmons
@RobKennedy-IMO not exactly correct: AFAIK, if you have a constructor with parameters, anything you do with its arguments in an initialization list can also be done in the body of a constructor with same members (albeit often with a bit more code). Using arguments to a constructor in an initialization list is not really very useful-it doesn't really seem to make much make sense: You pass arguments into a constructor like other functions: You're going to use them in the body of the function. Initialization lists are for initialization: setting defaults, constants,etc.Baruch
That's not true. A const member variable cannot be initialized in the constructor body; it must be initialized in the initialization list. Furthermore, there's nothing about the notion of "initialization" that precludes using run-time values, such as those from arguments. My constructor bodies are frequently empty because I've used the arguments to initialize members directly. Besides, if your argument for why initialization lists are obsolete is that we can just use the constructor body instead, then they were obsolete before C++11, too, and your question's premise falls apart.Emmittemmons
@RobKennedy - const members is a good case, agreed. if your argument for why initialization lists are obsolete... - not exactly: initialization lists allow you to use constructors with arguments to initialize your values, but in the body you have to call a method setValue(T) - the member has already been initialized with the default constructor when you pass the argument. But you can also do that with initialization in a header, just like in initialization lists.Baruch
Down-voter - please explain. Unexplained down-votes are not very constructive... Part of the answer to this question is fairly obvious, true - other parts, not so much...Baruch
D
94

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.

Dusky answered 10/6, 2014 at 20:18 Comment(5)
I think I would have also defaulted a and b, I wonder why Bjarne didn't?Uvula
@MooingDuck well a and b have different values across the different constructors and therefore do not have the same default value in each case. This is consistent with his logic.Dusky
a and b have differing values sure, but they do have the same default in each case. 7->a and 5->b. It's just that sometimes the default is overridden.Uvula
@MooingDuck that is a reasonable view but since it is a sparsely defined example, I don't have a strong view either way.Dusky
What about using HashingFunction hash_algorithm("MD5");, or HashingFunction hash_algorithm = hash_algorithm("MD5"); does that change anything? What if it was a plain data type, like doing int a = 7; to set the default value. Would using the = sign to set the default be equivalent to int a{7};?Overstuff
N
14

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.

Nashom answered 10/6, 2014 at 20:16 Comment(4)
Not sure I understand: What's wrong with this: B(int arg, bool b) {this->b=arg; ... }? No initialization list necessary. IMO using arguments to a constructor in an initialization list is not really very useful-it doesn't really seem to make much make sense: You pass arguments into a constructor like other functions: You're going to use them in the body of the function. Initialization lists are for initialization: setting defaults, constants,etcBaruch
@Baruch I disagree here. Why waste time initialising a data member (and a class-type data member will always be initialised) only to assign to it immediately after? Not to mention non-assignable types (e.g. const, references), which have to be "set up" by initialising. An mem-initialiser-list is for initialising, which the constructor should do. It shouldn't assign unless it needs to.Highpowered
@Angew - Why waste time initialising... I'd say "maybe" on that.. But const values are a good point.Baruch
@Baruch you're missing the point that all members are fully initialised before you enter the constructor body. They will be default-initialised if the initialiser list doesn't give them an explicit value. In the constructor body you're using operator= to reset the contents of each member, you aren't using the member's copy-constructor to set an initial value. Depending on the member's class, this could be costly and time-consuming. You should never give an object a value until you have the right value to give it. Ask Scott Meyers.Apportionment
H
7

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."

Highpowered answered 10/6, 2014 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.