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
).
Initialization. This is when values are put into the memory that was allocated. This is what the Constructor of a class does when using the new keyword. A variable must also be initialized by having the reference to some object in memory passed to it.
Initializing Instance MembersThe Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors. A final method cannot be overridden in a subclass.
The default value initialization occurs for all fields, including fields that have variable initializers. Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order.
A class has a class initialization method, which is like a constructor, but we cannot explicitly define the body of this method as we can for a constructor. Java does allow us to write arbitrary code for the initialization of class fields, however, with a construct known as a static initializer.
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 };
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 };
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With