C++ Member Initialization List

Why should I prefer to use member initialization lists?

For POD class members, it makes no difference, it's just a matter of style. For class members which are classes, then it avoids an unnecessary call to a default constructor. Consider:

class A
{
public:
A() { x = 0; }
A(int x_) { x = x_; }
int x;
};

class B
{
public:
B()
{
a.x = 3;
}
private:
A a;
};

In this case, the constructor for B will call the default constructor for A, and then initialize a.x to 3. A better way would be for B's constructor to directly call A's constructor in the initializer list:

B()
: a(3)
{
}

This would only call A's A(int) constructor and not its default constructor. In this example, the difference is negligible, but imagine if you will that A's default constructor did more, such as allocating memory or opening files. You wouldn't want to do that unnecessarily.

Furthermore, if a class doesn't have a default constructor, or you have a const member variable, you must use an initializer list:

class A
{
public:
A(int x_) { x = x_; }
int x;
};

class B
{
public:
B() : a(3), y(2) // 'a' and 'y' MUST be initialized in an initializer list;
{ // it is an error not to do so
}
private:
A a;
const int y;
};

Why use member init lists if there is default member initialization

There is no "must" here, a decision would probably be made based on circumstances.

For example, if the initialized value is not a constant, or if the value must be different in each of multiple constructors it makes sense to use the initializer list.

If you have to support pre-C++11 (and this may be more common than you think on large projects) you must always use the initializer list. Corresondingly if most of your code maintainers are not familiar with C++11 features it may be better to use the longstanding mechanism rather than the inline initialization.

If using C++11 is not an issue and you're initializing to a single constant value across multiple constructors, than the initializer form probably makes more sense.

C++ Member Initialization List

This is the initialization list :

Example::Example( int size, int grow_by) : m_size(5), m_top(-1)
{
... some code here
}

and it should be done only in the cpp file.

Don't you get an error when you do it like you did in the header in your example?

Order of member initialization list

Initialization of member variables occurs in the order that they are declared in the class.

http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-order

and:

http://en.cppreference.com/w/cpp/language/initializer_list

3) Then, non-static data members are initialized in order of
declaration in the class definition.

C++11 member initializer list vs in-class initializer?

No, these are not the same.

The difference between them is the same that applies for direct-initialization vs. copy-initialization, which is subtle but often very confusing.

§12.6.2 [class.base.init]:


  1. The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization. [...]

  2. In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

    — if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;

§8.5 [dcl.init]:


  1. The initialization that occurs in the form

    T x = a;


as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.

Initializing a non-static data member on a member-initializer-list follows the rules of direct-initialization, which doesn't create intermediate temporaries that need to be moved/copied (if compiled without a copy-elision), neither the type of the data member must be copyable/movable (even if the copy is elided). In addition, a direct-initialization introduces an explicit context, while a copy-initialization is non-explicit (if a constructor selected for the initialization is explicit, the program won't compile).

In other words, the obj s = obj("value"); syntax won't compile if obj is declared as:

struct obj
{
obj(std::string) {}
obj(const obj&) = delete;
};

or:

struct obj
{
obj(std::string) {}
explicit obj(const obj&) {}
};

As a more tangible example, while the below won't compile:

struct any
{
std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};

this one will:

struct any
{
std::atomic<int> a;
std::atomic<int> b{ 2 };
any() : a(1) {}
};

Which way is better (performance) ?

With a copy-elision enabled both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when the copy-initialization syntax is used (that obj s = obj("value"); is one of).


Is there another way ?

The brace-or-equal-initializer syntax allows one to perform a direct-list-initialization as well:

class any {
public:
obj s{ "value" };
any() {}
};

Are there any other differences?

Some other differences that are worth mentioning are:

  1. Brace-or-equal-initializer must reside in a header file along with a class declaration.
  2. If both are combined, member-initializer-list takes priority over brace-or-equal-initializer (that is, brace-or-equal-initializer is ignored).
  3. (C++11 only, until C++14) A class that uses brace-or-equal-initializer violates constraints for an aggregate type.
  4. With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization.

What's the differences between member initializer list and default member initializer on non-static data member?

Member initialization

In both cases we are talking about member initialization.
Keep in mind that the members are initialized in the sequence in which they are declared in the class.

Code 2: Member initializer list

In the second version:

Test() : count(10) {

: count(10) is a constructor initializer (ctor-initializer) and count(10) is a member initializer as part of the member initializer list. I like to think of this as the 'real' or primary way that the initialization happens, but it does not determine the sequence of initialization.

Code 1: Default member initializer

In the first version:

private:
int count=10;

count has a default member intitializer. It is the fallback option. It will be used as a member initializer if none is present in the constructor, but in the class the sequence of members for initialization is determined.

From section 12.6.2 Initializing bases and members, item 10 of the standard:

If a given non-static data member has both a
brace-or-equal-initializer and a mem-initializer, the initialization
specified by the mem-initializer is performed, and the non-static data
member’s brace-or-equal-initializer is ignored. [ Example: Given

struct A {
int i = / some integer expression with side effects / ;
A(int arg) : i(arg) { }
// ...
};

the A(int) constructor will simply initialize i to the value of arg,
and the side effects in i’s brace-or-equalinitializer will not take
place. —end example ]

Something else to keep in mind would be that if you introduce a non-static data member initializer then a struct will no longer be considered an aggregate in C++11, but this has been updated for C++14.


Differences

what's the differences of using one form rather than the other (if
any).

  • The difference is the priority given to the two options. A constructor initializer, directly specified, has precedence. In both cases we end up with a member initializer via different paths.
  • It is best to use the default member initializer because

    • then the compiler can use that information to generate the constructor's initializer list for you and it might be able to optimize.
    • You can see all the defaults in one place and in sequence.
    • It reduces duplication. You could then only put the exceptions in the manually specified member initializer list.

Initializer list vs Constructor assignment vs variable defining

This excerpt has been taken from "Inside the C++ Object Model" by Stanley B. Lippman.

You must use the member initialization list in the following cases in
order for your program to compile:

1. When initializing a reference member

2. When initializing a const member

3. When invoking a base or member class constructor with a set of arguments

4. A few efficiency cases. (Here the program is correct w/o member initialization list)

For points 1-3, member initialization list is a must.

For point 4, it is not compulsory.

For example(point 4), given :

class Word {
String _name;
int _cnt;
public:
// not wrong, just naive ...
Word() {
_name = 0;
_cnt = 0;
}
};

This implementation of the Word constructor initializes _name once, then overrides the initialization with an assignment, resulting in the creation and the destruction of a temporary String object.

A significantly more efficient implementation would have been coded:

// preferred implementation
Word::Word : _name( 0 )
{
_cnt = 0;
}

Due to this optimisation, a lot of people prefer member initialization list, as a default approach to write constructors.

// some insist on this coding style
Word::Word()
: _cnt( 0 ), _name( 0 )
{}

A reasonable question to ask at this point is, what actually happens to the member initialization list?

The compiler iterates over the initialization list, inserting the initializations in the proper order within the constructor prior to any explicit user code.

For example, the previous Word constructor is expanded as follows:

// Pseudo C++ Code
Word::Word( /* this pointer goes here */ )
{
_name.String::String( 0 );
_cnt = 0;
}

Note : The order in which the list entries are set down is determined
by the declaration order of the members within the class declaration,
not the order within the initialization list. In this case, _name is
declared before _cnt in Word and so is placed first.

So coming back to your question :

class B is fine(since you are using primitive datatypes).

class A will generate the same code as class B

As for class C, the default constructor is first called, and then initialization of width and height would happen. This method should be preferred when there are going to be more than 1 constructor, and for each constructor width and height need to be defaulted to your desired values.

However, since the advent of C++11, and use of {} as uniform initialization, a more recommended approach for writing class C would be :

class C
{
public:
int width {500};
int height {300};
};

C++ using 'this' keyword in member initialization list

This works fine but instead I wanted to use value name for the parameter too and this causes a name clash.

No it doesn't.

Integer(int value) : value(value){ }

This is just fine (if a little confusing to some).

It is simply not permitted in the language grammar to write this-> there, probably because it would make no sense to specify any other object: the this-> is implicitly done for you.

C++ - Mixing default member initializers and member initialization lists - bad idea?

The presence of default member initializers changes nothing about the order in which subobjects of a type are initialized. It will always be in declaration order.

Style is up to you. The more constructors you have, the more you gain by using DMIs, since you're not repeating initialization that doesn't change. At the same time, if you start making constructors that override DMIs, that can create confusion about what the object's initial state is. The key is to try not to be surprising about what's going on.

In your particular case however, I'd say that you have too many variables. Your array should just be a std::vector<uint8_t>. Having the header_ pointer is dubious but defensible (though it's initialized incorrectly; you need to use placement-new to satisfy C++'s object model). But data_length_ can be computed on an as-needed basis.

The fewer members you have, the less chance for confusion about how they get initialized.



Related Topics



Leave a reply



Submit