I cannot understand what the use is of delegating constructors. Simply, what cannot be achieve without having delegating constructors?
It can do something simple like this
class M
{
int x, y;
char *p;
public:
M(int v) : x(v), y(0), p(new char [MAX]) {}
M(): M(0) {cout<<"delegating ctor"<<endl;}
};
But I don't see it is worth introduce a new feature for such a simple thing? May be I couldn't recognize the important point. Any idea?
One key use of delegating constructors that doesn't merely reduce code duplication is to obtain additional template parameter packs, particular a sequence of integer indices, needed to specify a member initializer:
For example:
In this way we can define a constructor that initializes the array to a constant value without first default initializing each element. The only other way to achieve this, as far as I know, would require sticking the data member in a base class.
Of course, better language support for template parameter packs might make this unnecessary.
I described another use of delegating constructors in Overload #113 which simplifies the solutions described in Cassio Neri's Complex Logic in the Member Initialiser List in Overload #112.
Unlike code inside a function body, when you are writing a constructor's member initialisers you cannot create a local variable to store an intermediate result that is needed by more than one of the members.
Consider a constructor like this:
We want to avoid performing the expensive calculation twice (and in the context of the original problem described by Cassio, a base class also wants the result of the calculation, so you cannot simply assign to
x_
andy_
in the constructor body).The trick I described is to calculate the intermediate result and delegate to another constructor that uses that result:
It seems to me that it's worth mentioning that it has occasionally been suggested that the code duplication between multiple constructors can be alleviated by refactoring the common code into a private init-function. The problem with that is that if the class has friends, those friends can invoke the init multiple times - and it's not supposed to be invoked multiple times. Delegating constructors prevent such problems by the virtue that constructors cannot run after the object has been initialized already.
In addition to quantdev's excellent answer (which I have upvoted), I wanted to also demonstrate the exception safety issues of delegating constructors for those types which must explicitly acquire multiple resources in a constructor, and explicitly dispose of multiple resources in its destructor.
As an example, I will use simple raw pointers. Note that this example is not very motivating because the use of smart pointers over raw pointers will solve the problem more neatly than delegating constructors. But the example is simple. There still exists more complex examples that are not solved by smart pointers.
Consider two classes
X
andY
, which are normal classes, except that I've decorated their special members with print statements so we can see them, andY
has a copy constructor that might throw (in our simple example it always throws just for demonstration purposes):Now the demo class is
Z
which holds a manually managed pointer to anX
and aY
, just to create "multiple manually managed resources."The
Z(const X& x, const Y& y)
constructor as it stands is not exception safe. To demonstrate:which outputs:
X
got constructed twice, but destructed only once. There is a memory leak. There are several ways to make this constructor safe, one way is:The example program now correctly outputs:
However one can easily see that as you add managed resources to
Z
, this quickly gets cumbersome. This problem is solved very elegantly by delegating constructors:This constructor first delegates to the default constructor which does nothing but put the class into a valid, resource-less state. Once the default constructor completes,
Z
is now considered fully constructed. So if anything in the body of this constructor throws,~Z()
now runs (unlike the previous example implementations ofZ(const X& x, const Y& y)
. And~Z()
correctly cleans up resources that have already been constructed (and ignores those that haven't).If you have to write a class that manages multiple resources in its destructor, and for whatever reasons you can't use other objects to manage those resources (e.g.
unique_ptr
), I highly recommend this idiom to manage exception safety.Update
Perhaps a more motivating example is a custom container class (the std::lib doesn't supply all containers).
Your container class might look like:
One way to implement the member-template constructor is:
But here is how I would do it:
If someone in code review called the latter bad practice, I would go to the mat on that one.
Delegating constructors prevent code duplication (and all the possible errors and flaws that come with it : increased maintenance, decreased readability...), which is a good thing.
It is also the only way to delegate the initialization list (for members and bases initializations), i.e. you really can't replace this feature by having a shared
Init()
method for your constructors.Examples:
1) Common initialization from N1986 proposal :
2) Delegation with both constructor and copy constructor, also from N1986 proposal :
3) MSDN gives this example, with constructors performing argument validation (as commented, this design is debatable) :
Thanks to constructors delegation, it reduces to :
Links: