A great many classes I see have default constructors. A great many of those are implemented by zeroing out the class members for them to be 'initialised' properly at a later time.
Let's remind ourselves what constructors are for, from the inventor of C++ himself:
It is the job of every constructor to establish the class invariant, so that every member function can rely on it.
A default constructor is one that takes no arguments. That suggests that there is a state which can be arrived at without user configuration and which doesn't violate the class invariant. And for that to be the case, one of the following must be true:
- You have a well-defined invariant which allows a naturally 'empty' state.
- You have a weakened invariant which allows a singular state.
Containers are a good example of the first case. Their invariant is such that an empty container is valid and useful, and it can be arrived at easily without any configuration.
A class which zeroes its members in the constructor and requires a further initialisation step is an example of the second case. The majority of operations that may be done on this post-default-construction, pre-initialisation object are illegal, which makes this default state singular.
If this singular state is so useless, why not avoid it entirely by establishing useful states in all constructors and strengthening your class invariant? If a default constructor isn't capable of achieving this, don't provide one.
More on this to come.
I got annoyed by this exact thing today because the Qt framework assumes it can default construct objects, which means you have to provide a default constructor, even if the class would make much more sense if it did not have one. e.g. a button class that derives from a Qt button class but you want it to always be given a delegate of some kind as its 'button action'. The only option is to allow a 'null action' and have a mutator for providing the real one later. Crap!
ReplyDelete