Initializing data members inside class body - c++

When data members should be initialized directly inside class/struct body over using constructor?
struct A{
int x;
A() : x{3} {}
};
struct B{
int x{3};
};
The above two methods have the same effect.

Member init list must be used if the value depends on a constructor argument. It must also be used if separate constructors should initialise the member with different value. It must also be used prior to C++11, because that is the language version where the default member initialisers were introduced.
Otherwise the choice is up to the programmer. Default member initialisers are useful for avoiding repetition in constructors that initialise with the same, constant value, as well as having more concise and simpler syntax.

I would like to point out one difference.
struct B{
int x{3};
};
The above initialization applies to all the constructors where x is not explicitly initialized.
The below initialization applies ONLY to the default constructor. So if you are following below approach, you may end up with lot of boiler plate code when the value initialized is same.
struct A{
int x;
A() : x{3} {}
};

Related

C++ Initialising fields directly vs initialisation list in default constructor

I'd like to know if there is a difference between this code:
class Foo{
private:
int a = 0;
public:
Foo(){}
}
And:
class Foo{
private:
int a;
public:
Foo(): a(0) {}
}
And, if so, which should be preferred?
I know it's preferable to use an initialiser list than assigning in the constructor body, but what about initialiser list vs directly initialising in field declaration (for primitive types, at least, as is the case here)?
Also, what of the case below:
class Foo{
private:
int a = 0;
public:
Foo(){}
Foo(int i): a(i) {}
}
When the non-default constructor is called: is "a" initialised twice, first to 0 then to "i", or directly to "i"?
From cppreference - Non-static data members
Member initialization
1) In the member initializer list of the constructor.
2) Through a default member initializer, which is simply a brace or equals initializer included in the member declaration, which is used if the member is omitted in the member initializer list.
If a member has a default member initializer and also appears in the member initialization list in a constructor, the default member initializer is ignored.
To conclude, both initializers are equivalent and do what they are supposed to do.
I would prefer the default member initializer, if I'd use the default constructor anyway, or if all or most constructors would initialize the member to the same value.
class Foo {
private:
int a = 0;
};
If all constructors initialize the member to some different value however, using the default member initializer makes less sense, and then an explicit initialization in the respective constructors would be more clear
class Foo {
private:
int a;
public:
Foo() : a(3) {}
Foo(int i) : a(i) {}
};
The first set of examples are identical to each other.
For the last example, the C++ standard specifies as follows:
12.6.2 Initializing bases and members
[ ... ]
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-equal-initializer will not take
place. — end example ]
The two are identical.
One rule of software engineering is DRY -- don't repeat yourself. DRY states that if you can avoid repeating the same token twice, or having two identical lists, you should.
This is for a few reasons. Maintaining two identical lists is surprisingly error prone; one gets modified, or has a typo, and the other does not. It makes code longer, which can make it harder to read. And avoiding copy-paste coding encourages using some very powerful and expressive techniques that can make what you are doing clearer than doing it manually 17 times.
struct foo {
int a;
foo():a(7) {}
};
here we have repeated ourselves -- the list of member variables, in particular, is listed twice. Once in the definition of foo, and again in the initializer list of foo::foo. If it is missing somewhere, you get uninitialized data.
struct foo {
int a = 7;
foo() {}
};
Here we do not repeat ourselves.
struct foo {
int a = 7;
foo() {}
foo(int i):a(i) {}
};
Here there is some repetition, but the repetition is unavoidable. It is, however, minimized.
There is some cost here, because someone might interpret a=7 to mean "it always starts at 7", and not "the default is 7".
struct foo {
int a = 7;
foo():a(3) {}
foo(int i):a(i) {}
};
And the above is a horrible anti-pattern.

Why should a derived class's constructor use the base's default constructor in it's initializer list?

Here is an example of my question:
class MyBaseClass
{
public:
MyBaseClass(): my_bool(false), my_value(0)
{}
MyBaseClass(bool b, int i): my_bool(b), my_value(i)
{}
private:
bool my_bool;
int my_value;
}
class MyDerivedClass1 : public ::MyBaseClass
{
public:
MyDerivedClass1(double d): my_double(d)
{}
private:
double my_double;
}
class MyDerivedClass2 : public ::MyBaseClass
{
public:
MyDerivedClass2(double d): MyBaseClass(), my_double(d)
{}
private:
double my_double;
}
Why isn't the MyDerivedClass1 an ok way to initialize my derived class versus having to explicitly initialize the base class like in MyDerivedClass2?
I guess I don't understand why I can't just rely on C++ calling my base constructor? I know if I wanted them initialized to something different I'd have to call the other constructor in my initialization list, but all I want is the base constructor to be called anyway.
There is no difference between providing a default-constructed base class in the initializer list or not providing it. What you use if entirely your style. (or the company)
In general, I would say you have 2 options (let's keep constructors out of scope), assuming you always initialize your members.
Option 1: Initialize all members in the init-list.
This option should be used if you are C++98-compatible. It has as advantage that you are defining all construction parameters in a single list, making it easy to search through. (Unless you have 50+ members)
The disadvantage of this option is a lot of duplication when you have multiple constructors.
With this, you can have 3 variants for them:
Skip the default-initialized classes, this makes the list shorter though it's hard to check if you have intended to 'forget' it. (Think replacing a class by a ptr to it)
Default initialize all members, this makes the list longer, though indicates clearly the intent
Explicitly provide the class you are initializing, by copy contructing with a tempuary
Option 2: Initialize all members on declaration, except for constructor parameters
This option assumes you initialize everything in the class declaration. Yet again you can explicitly call the default constructor or not, preferably with braced init, as round braces are interpreted as a function declaration.
In the initializer list you only have to put the members which are linked to the construction parameters.
The advantage of this option is readability (especially for large classes). The disadvantage is that this limits your compiler options to 'modern' compilers.
Base classes
If we again consider base classes and look at them as if they were members, the consistent way would be to declare them explicitely for option 1 and don't write them for option 2.
Personnally I like option 2, as I too often encounter classes with too many members and checking if all members are initialized is easier by hand with this one.
Option 1 however is often used because of legacy in order to keep the code consistent.
POD
Important to mention are the PODs (Plain old datatype), like int, double ...
If you don't initialize them you get some random data. This makes it important to explicitly intialize them, regardless of the method you use to do so.
Conclusion
So in the end, it's all a matter of style with no functional difference.
Although in most cases there isn't a semantic difference and the issue is mostly one of style, there is one case where it is a difference: when the default constructor of the base class is not user-provided.
The difference comes from the fact that a base that is not in the member initializer list is default-initialized, while explicitly writing Base() value-initializes it, which performs zero-initialization if the default constructor isn't user-provided.
Thus:
struct A { int i; };
struct B : A {
B() : j(i) {} // may be undefined behavior
int j;
};
struct C : A {
C() : A(), j(i) {} // OK; both i and j are zero
int j;
};

When to use which data member initialization in C++

Considering this program:
#include <iostream>
class C
{
public:
C(void): a(1)
{ a=2; }
int a{3};
};
int main(void)
{
C c{};
std::cout << c.a; // 2
}
I can see three forms of data member initialization:
using a member initializer list
using the Constructor
using a declaration in the class body
When to use which?
1: Using a declaration in the class body
You should use this when the member will always be initialized with the same value, and it doesn't make sense to have to explicitly write that for each constructor.
2: Using a member initializer list
The member initializer list is obviously necessary for a member that lacks a default constructor, but aside from that, if you're initializing a member based on the constructor, it makes sense to do it here.
3: Using the constructor body
The constructor body is more useful for logic that can't be performed in a single statement (in the init-list). However, I don't think there is much difference between initializing a POD in the member initializer list or the constructor body.
My suggestion is to use:
int a{3};
This makes sure that a is initialized to 3 no matter how many constructors you have in the class.
My second choice will be to use the member initialization list.
c(void) : a(1) {}
The third option, using code to set the value of a member variable, should be avoided.

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

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

Why are member variables visible in c++ constructor initialization lists?

I came across this due to a bug in my code and I'm curious why it's allowed. What reason is there that allows object members to be visible in the constructor initialization list?
#include <stdio.h>
class derived {
private:
int * value2;
public:
derived();
};
derived::derived()
: value2(value2){} // Uninitialized self-assignment
int main()
{
derived thisChild;
}
Clang gives a warning about this but unfortunately g++ does not.
So you can initalise one member using another; this is perfectly fine if the other has already been initialised, or if you're just using its address to initialise a pointer or reference. For example:
struct Thingy
{
int & r;
int a;
int b;
Thingy(int x) :
r(a), // OK - a has a valid address, and we're not using the value
a(x),
b(a) // OK - a has been initialised
{}
};
It would be rather tricky to allow that and disallow your example.
If it is not visible, you cannot write this:
A(int n) : some_num(n), another_num(some_num*10) {}
then what would be the point of member-initialization list?
As for self-initialization (uninitialized variable), you can do even this:
int xyz = xyz; //will compile
You can think of the initialization list as part of the body of the method (specifically the constructor), so it is natural that you can access the member variables of an object in one of its methods
Also, you might want to reuse an already created member variable to initialize others -- note: you will have to know the exact order of initialization (order of member variable declaration) to make sure you are using this portably
I think you mean why are member variables accessible in initializer expressions.
One reason is that the initialization of one member variable can depend on another.
That's fragile, but sometimes necessary to avoid awkward code such as artificial base classes.