64

Regarding the following, are there any reasons to do one over the other or are they roughly equivalent?

class Something
{
    int m_a = 0;
};

vs

class Something
{
    int m_a;
    Something(int p_a);
};

Something::Something(int p_a):m_a(p_a){ ... };
Mark B
  • 95,107
  • 10
  • 109
  • 188
Evan Ward
  • 1,371
  • 2
  • 11
  • 23
  • 8
    first one is new feature of c++11 – Emadpres Feb 09 '15 at 15:25
  • 1
    I don't expect the first code to work. (edit: ok it does now) – Aitch Feb 09 '15 at 15:25
  • 1
    possible duplicate of [C++11 allows in-class initialization of non-static and non-const members. What changed?](http://stackoverflow.com/questions/13662441/c11-allows-in-class-initialization-of-non-static-and-non-const-members-what-c) – Jonathan Mee Feb 09 '15 at 15:37

7 Answers7

75

The two code snippets you posted are not quite equal.

class Something
{
    int m_a = 0;
};

Here you specify the value with which to initialise, i.e. 0, at compile time.

class Something
{
    int m_a;
    Something(int p_a);
};

Something::Something(int p_a):m_a(p_a){ ... };

And here you do it at run time (or possibly at run time), with the value p_a not known until the constructor is called.

The following piece of code comes closer to your first example:

class Something
{
    int m_a;
    Something();
};

Something::Something() : m_a(0) { /* ... */ };

What you have to consider here is that in the first case, the value appears directly in the class definition. This may create an unnecessary dependency. What happens if you need to change your 0 to 1 later on? Exposing the value directly in the class definition (and thus, usually, in a header file) may cause recompilation of a lot of code in situations where the other form of initialisation would avoid it, because the Something::Something() : m_a(0) part will be neatly encapsulated in a source file and not appear in a header file:

// Something.h - stable header file, never changed
class Something
{
    int m_a;
    Something();
};

// Something.cpp - can change easily
Something::Something() : m_a(0) { /* ... */ };

Of course, the benefits of in-class initialisation may vastly outweigh this drawback. It depends. You just have to keep it in mind.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 5
    +1 for the point about unintentionally causing expensive recompiles--these can get pretty expensive pretty quickly. – sudo make install Nov 13 '16 at 12:05
  • noteable here: the compiler creates different symbols for each case, and the linker places each variable into a different section – clockw0rk Sep 11 '19 at 13:21
19

The first form is more convenient if you have more than one constructor (and want them all to initialise the member in the same way), or if you don't otherwise need to write a constructor.

The second is required if the initialiser depends on constructor arguments, or is otherwise too complicated for in-class initialisation; and might be better if the constructor is complicated, to keep all the initialisation in one place. (And it's also needed if you have to support pre-C++11 compilers.)

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
7

The first form is new to C++11 and so at this point isn't terribly well supported, especially if you need to support a variety of older compilers.

Otherwise they should be roughly equivalent when a C++11 compiler is available.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I thought we've always been able to initialize `int`s like the first form. Does C++11 now support initializing more than `int`s that way?!? – Jonathan Mee Feb 09 '15 at 15:32
  • 1
    @Jonathan Mee You could always initialize `static` ints that way previously, yes. – Mark B Feb 09 '15 at 15:35
  • 2
    @JonathanMee: Non-static member initialisation is new to C++11. It also extends static member initialisation to any constant literal types, not just integers. – Mike Seymour Feb 09 '15 at 15:38
5

Elaborating on Christian Hackl's answer.

The first form allows to initialize m_a and have a default c'tor at the same time. Or you can even be explicit in your code and define a constructor with the default keyword:

class Something
{       
    int m_a = 0;

    // explicitly tell the compiler to generate a default c'tor
    Something() = default;
};

With the second form, an auto-generated default c'tor would leave m_a uninitialized, so if you want to initialize to a hard-coded value, you have to write your own default c'tor:

class Something
{
    int m_a;

    // implement your own default c'tor
    Something() : m_a(0) {}
};
Alexey Polonsky
  • 1,141
  • 11
  • 12
0
class Something
{
    int m_a = 0;
};

is equivalent to

class Something
{
    int m_a(0);
};

So, doing

class Something
{
    int m_a;// (0) is moved to the constructor
public:
    Something(): m_a(0){}
};

yields a uniform syntax for initialization that requires or does not require run-time input.

Personally I don't like the first form because it looks like an "declaration then assignment", which is complete misconception.

user3528438
  • 2,737
  • 2
  • 23
  • 42
0

If you change the code like what Christian did to make them do the same thing. Now it is a tradeoff between compile-time optimization (which Christian explained completely) and run-time optimization.

class Foo{

public: 
 Vector<double> vec1;
 .
 .
 .
  Vector<double> vecN;
}

Imagine you have to initialize each vector by some predefined doubles. If the program must instantiate many and many objects of this class, it is better to initialize the vectors in the header file to make the user happy by reducing the run-time!

hosh0425
  • 98
  • 1
  • 10
-6

Even though it's supported, this type of initialization will create bugs that will be pretty hard to track down. It's the type of "aesthetic optimization" that you will regret a couple months down the road.

See the example below:

class_x_1.h:

class X
{
private:
    int x = 10;
public:
     int GetX();
};

class_x_2.h:

class X
{
private:
    int x = 20;
public:
    int GetX();
};

class_x.cpp:

#include "class_x_1.h" // implementation uses the version that initializes x with 10

int X::GetX()
{
    return x;
}

main.cpp:

#include "class_x_2.h" // main includes definition that initializes x with 20

#include <iostream>

int main()
{
    X x;
    std::cout << x.GetX() << std::endl;
    return 0;
}

Output:

20

As expected, it will return 20 because this is the version that initializes x with 20. However, if you go to the implementation of X, you might expected it to be 10.

SeeSharpr
  • 193
  • 1
  • 8