I\'d like to know if there is a difference between this code:
class Foo{
private:
int a = 0;
public:
Foo(){}
}
And:
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 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.
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 ]