Why should I initialize member variables in the order they're declared in?
I was writing some code today and got a weird compile error, which seems to be caused by initializing member variables in a different order than they were declared.
Example:
class Test {
int a;
int b;
public:
Test() : b(1), a(2) {
}
};
int main() {
Test test;
return 0;
}
Then if I compile it with -Werror -Wall
:
$ g++ -Werror -Wall test.cpp
test.cpp: In constructor ‘Test::Test()’:
test.cpp:3:9: error: ‘Test::b’ will be initialized after [-Werror=reorder]
test.cpp:2:9: error: ‘int Test::a’ [-Werror=reorder]
test.cpp:6:5: error: when initialized here [-Werror=reorder]
cc1plus: all warnings being treated as errors
I realize that -Wall
is explicitly asking GCC to go over-the-top with warnings, but I assume there's a reason for all of them. So, how could the order of initializing member variables matter?
Solution 1:
The reason is because they're initialized in the order they're declared in your class, not the order you initialize them in the constructor and it's warning you that your constructor's order won't be used.
This is to help prevent errors where the initialization of b
depends on a
or vice-versa.
The reason for this ordering is because there is only one destructor, and it has to pick a "reverse order" to destroy the class member. In this case, the simplest solution was to use the order of declaration within the class to make sure that attributes were always destroyed in the correct reverse order.
Solution 2:
Why should I initialize member variables in the order they're declared in?
The members will be initialized in the same order they are declared, whether you want it or not. The warning is telling you that the order you are asking for differs from the actual order of execution of initialization.
Solution 3:
You shouldn't because it decreases readability and is potentially misleading.
If you did:
Test() : b(1), a(b) {}
it would appear that b
then a
were both set to 1
, whereas actually the uninitialized value of b
is used to initialize a
before b
is initialized to 1
.
Solution 4:
Actually the compiler always initializes the variables in the order of declaration, even if you write the initializers in a different order. Therefore if you don't write the initializations in the order of declaration, the order of your initializers does not fit the order of initialization, which may lead to subtle bugs if the initialisations depend on each other.
For example, consider the code
Test(): b(42), a(b) {}
This is a bug because a
is initialized before b
, but it looks OK. If you write it in the order of declaration (which is the order of initialization), the bug gets obvious:
Test(): a(b), b(42) {}
Note that the bug can also be subtler than that; for example imagine a
and b
are class types which output something in their constructor; then with the "incorrect" order you'd think that b
's output should appear before a
's when in reality the reverse will happen. If a
's output appearing first will lead to an invalid file, that's also a bug, but there's no way the compiler could notice the problem if the constructors are in another translation unit (apart from the fact that the compiler cannot know if reordering is or is not a bug). Therefore it's reasonable that the compiler just warns about every instance of non-matching order.