10

In C++ it is possible to initialize values of class's fields durectly in class, like:

class X
{
  int a = 5;
}

What's the reason for it? Where it can be useful? The default ctor does exactly the same. And it seems like I cannot initialize values with bit masks (int a : 3).

SergeyA
  • 61,605
  • 5
  • 78
  • 137
yanpas
  • 2,155
  • 1
  • 17
  • 26

2 Answers2

16

From the authority (this reads pretty similar to the earlier standard-proposal N2756):

In-class member initializers

In C++98, only static const members of integral types can be initialized in-class, and the initializer has to be a constant expression. These restrictions ensure that we can do the initialization at compile-time. For example:

int var = 7;

class X {
    static const int m1 = 7;        // ok
    const int m2 = 7;                   // error: not static
    static int m3 = 7;              // error: not const
    static const int m4 = var;          // error: initializer not constant expression
    static const string m5 = "odd"; // error: not integral type
    // ...
};

The basic idea for C++11 is to allow a non-static data member to be initialized where it is declared (in its class). A constructor can then use the initializer when run-time initialization is needed. Consider:

class A {
public:
    int a = 7;
};

This is equivalent to:

class A {
public:
    int a;
    A() : a(7) {}
};

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:

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

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

If a member is initialized by both an in-class initializer and a constructor, only the constructor's initialization is done (it "overrides" the default). So we can simplify further:

class A {
public:
    A() {}
    A(int a_val) : a(a_val) {}
    A(D d) : b(g(d)) {}
    int a = 7;
    int b = 5;  
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
};
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
5

Typically, your class definition is in a header file (.h), and your Constructor is in an implementation file (.cpp).

Several times I have seen a bug where the header file has a long list of member variables, and the implementation file initializes them.... but accidentally skips one member, resulting in a bug.

On visual inspection, the code looks right. Lots of members declared; lots of members initialized. The absence of one single value is not obvious.

By putting all the initialization in the same place as the declaration, it is much easier to see if you forgot to initialize one member.


class MySample
{

private:
    int m_CountSamples       { 0 };
    int m_SampleWidth        { sizeof(int) };
    double m_SamplePrecision { 3.14 };
    bool m_fieldIsSorted;                       // It is obvious which field got skipped!
    enumMeaning fieldMeaning { eProductionSample };
};    
abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 2
    So many fields that you can forget to initialize something sounds like a code smell. The class probably has too many responsibilities. – Sebastian Mach Feb 23 '16 at 15:59
  • 5
    I am working on a nearly 30 year old code base... it has lots of code-smell. But that is reality. Not all code is clean. – abelenky Feb 23 '16 at 16:01
  • 2
    @phresnel, that's a possibility but it is not unusual for classes to have more than five member variables as part of their definition. – R Sahu Feb 23 '16 at 16:01
  • @RSahu: Not unusual in legacy and non-OOP code indeed (I know a megaton of that). The smell still smells though. Anyways, was that really a reason to introduce this, or is this speculation? – Sebastian Mach Feb 23 '16 at 16:03
  • @phresnel, It also depends on the complexity of the data involved in implementing some functionality. I just pulled one of the classes at my work and it has 49 member variables, many of which are `std::map`s, `std::set`s, and `std::vector`s. – R Sahu Feb 23 '16 at 16:08
  • @RSahu: Which data-structure did it implement? That does really not sound clean (and OOP'esque) at all. With so many members (never seen that, even in the wildest legacy code and diabloc state machines), I bet my soul that there is no clean separation of concerns. – Sebastian Mach Feb 23 '16 at 16:17
  • @phresnel, it is responsible for gathering all the data necessary from a generic modeling framework that is needed for an optical analysis and export the data for an external optical analysis tool -- Code V, if you are interested. It has a very narrow and well defined purpose but it needs to deal with a lot of types of data. – R Sahu Feb 23 '16 at 16:19
  • @RSahu: Sounds like legacy code and not really complex. Even if you must fetch and transform thousands of data-sources, there is no inherent need to micromanage 50 member variables. Are you sure things can't be transformed into grouping like `Scene <- {Camera, Screen, Geometry}` and so on? – Sebastian Mach Feb 23 '16 at 16:26
  • @phresnel: Your on-going discussion has absolutely nothing to do with the question at hand. This is not appropriate commentary. – abelenky Feb 23 '16 at 16:27
  • @phresnel, believe me, I would if I could. – R Sahu Feb 23 '16 at 16:27
  • @abelenky: When we're really strict, your answer is not an answer, because the question was "What's the reason", not "What could be the reason". Anyways, this is indeed becoming too long. – Sebastian Mach Feb 23 '16 at 16:37
  • 1
    As a matter of fact, it doesn't matter how many members are in the class. If there is a fixed number the member has to be set to (not something supplied through contstructor argument) the member initialized where it is declared is of a huge help to code supportability. – SergeyA Feb 23 '16 at 16:40