@SebastianRedl already gave the simple, direct answers, but some extra explanation might be useful.
TL;DR = there's a style rule to keep constructors simple, there's reasons for it, but those reasons mostly relate to a historic (or simply bad) style of coding. Handling of exceptions in constructors is well defined, and destructors will still be called for fully-constructed local variables and members, which means there shouldn't be any problems in idiomatic C++ code. The style rule persists anyway, but normally that's not a problem - not all initialization has to be in the constructor, and particularly not necessarily that constructor.
It's a common style rule that constructors should do the absolute minimum they can to set up a defined valid state. If your initialization is more complex, it should be handled outside the constructor. If there's no cheap-to-initialize value that your constructor can set up, you should weaken the invarants enforced by your class to add one. For example if allocating storage for your class to manage is too expensive, add a not-allocated-yet null state, because of course having special-case states like null never caused anyone any problems. Ahem.
Although common, certainly in this extreme form it's very far from absolute. In particular, as my sarcasm indicates, I'm in the camp that says weakening invariants is almost always too high a price. However, there are reasons behind the style rule, and there's ways to have both minimal constructors and strong invariants.
The reasons relate to automatic destructor cleanup, particularly in the face of exceptions. Basically, there has to be a well-defined point when the compiler becomes responsible for calling destructors. While you're still in a constructor call, the object isn't necessarily fully constructed, so it's not valid to call the destructor for that object. Therefore, the responsibility for destructing the object only transfers to the compiler when the constructor successfully completes. This is known as RAII (Resource Allocation Is Initialization) which isn't really the best name.
If an exception throw occurs inside the constructor, anything part-constructed needs to be explicitly cleaned up, typically in a try .. catch
.
However, components of the object which have already successfully constructed are already the compilers responsibility. This means that in practice, it's not really a big deal. e.g.
classname (args) : base1 (args), member2 (args), member3 (args)
{
}
The body of this constructor is empty. So long as the constructors for base1
, member2
and member3
are exception safe, there's nothing to worry about. For example, if the constructor of member2
throws, that constructor is responsible for cleaning itself up. The base base1
was already completely constructed, so its destructor will be automatically called. member3
was never even partially constructed, so doesn't need cleanup.
Even when there's a body, local variables that were fully constructed before the exception was thrown will be automatically destructed, just like any other function. Constructor bodies that juggle raw pointers, or "own" some kind of implicit state (stored elsewhere) - typically meaning a begin/acquire function call must be matched to an end/release call - can cause exception safety problems, but the real problem there is failing to manage a resource properly via a class. For example if you replace raw pointers with unique_ptr
in the constructor, the destructor for unique_ptr
will be called automatically if needed.
There's still other reasons people give for preferring do-the-minimum constructors. One is simply because the style rule exists, many people assume constructor calls are cheap. One way to have that, yet still have strong invariants, is to have a separate factory/builder class that has the weakened invariants instead, and which sets up the needed initial value using (potentially many) normal member-function calls. Once you have the initial state you need, pass that object as an argument to the constructor for the class with the strong invariants. That can "steal the guts" of the weak-invariants object - move semantics - which is a cheap (and usually noexcept
) operation.
And of course you can wrap that in a make_whatever ()
function, so callers of that function never need to see the weakened-invariants class instance.
Square
, with a constructor that takes one parameter, the length of a side, you want to check if that value is greater than 0.