Is it a good idea to write a default constructor for all classes?
No. If there is no sensible “default value” for your type, you should not write a default constructor. Doing so would violate the principle of least surprise.
That said, many types do have a sensible default value.
- the number zero (more generally: the neutral element)
- the empty string
- an empty list
- a 0 × 0 matrix
- the time-zone UTC ± 00:00
- …
For such types, you really should define a default constructor.
Other types don't have a natural default value but have an “empty” state that can be reached by performing certain operations. It is sensible to default-construct such an object to have that state.
- an I/O stream disconnected from any source / sink that fails every operation (can be reached by reaching the end of the file or encountering an I/O error)
- a lock guard that doesn't hold a lock (can be reached by releasing the lock)
- a smart pointer that doesn't own an object (can be reached by releasing the managed object)
- …
For these types, it is a trade-off whether to define a default constructor. Doing so does no harm but makes your type slightly more complicated. If it is a public type found in a library, then it is probably worth the trouble. If you're going to implement a move constructor (and assignment operator) anyway, you can equally well define the default constructor to construct the object in a state that would be reached by moving away from it.
For other types, you just cannot define a sensible default.
- a day of the week
- a name for a baby
- …
Do not invent an artificial “null” state for these types just to make them default-constructible. Doing so complicates the code and forces you to provide less useful class invariants as you could without that artificial state. If somebody really needs that additional state, they can easily use an optional<T>
but going the other way round is not possible.
Aren't there certain parts of the STL that won't work if your classes don't have default constructors?
Yes. std::vector::resize
is probably the most prominent example. But don't worry about these. If your type does not have a sensible default value, performing that operation isn't meaningful either. It's not the fault of your type. It's inherent to the nature of the concept you're trying to model.