Power Programming Languages

Power Programming Languages

IBM Power, including the AIX, IBM i, and Linux operating systems, support a wide range of programming languages, catering to both traditional enterprise applications and modern development needs.


#Power

 View Only

C++0x delegating constructors

By Archive User posted Wed December 03, 2008 02:13 PM

  

Originally posted by: Michael_Wong


Something that Java, C++/CLI, and C# already has, is called delegating constructor. This is the ability for one constructor to delegate to another within the same class.C++03 does not have this capability because you cannot name a constructor within the same class in one of its constructor's initializer list. It can use constructors of other class types, including base classes and member objects.

In fact, it causes a serious problem when people who learn these other language come to C++, and try using it because what looks reasonable, will compile and run, but does not do what they think it does.

And it is more common then you think, because in many cases constructors may differ by the parameter list with common bodies. If the constructor can use default arguments, you could common the bodies. If it can't then you are out of luck today:

class A {};//has a conversion operator to double
class C
{
int x;
double y;
void startup();
public: //C(): x(0), y(1.23) { startup();} // I can collapse this into the following constructor and common up the body
C(int i=0) : x(i), y(1.23) { startup();}
C (const A& a) : x(0), y(a) {startup();} // I can't with this one
};


So having successfully refactored once, you might decide to try it in another case, turning this code into:
class X {  
Y y_;
Z z_;
void init();
public:
X() { init() };
X( int i ) : y_( i ),z_(1.23) { init(); }
X( Widget w): y_(3.21), z_(1.23) { init(); }
};


this because this is similar to Java delegating constructors:

class X {  
Y y_;
Z z_;
void init();
public:
X(){ init(); };
X( int i ) : y_( i ),z_(1.23) { X(); }//I know both constructors are run because I can see it in a debugger
X( Widget w): y_(3.21), z_(1.23) { X(); } //same here
};


This should work. Or should it ?

Well of course it doesn't, and the Mystery#1 of the Week is can someone tell me why not?

So may be we want to try something even more ambitious:

class X {  
Y y_;
Z z_;
void init();
void *operator new(unsigned int, const X*);
... //assume we also have corresponding delete
public:
X(){ init(); };
X( int i ) : y_( i ),z_(1.23) { new (this) X; }//I know both constructors are run because I can see it in a debugger
X( Widget w): y_(3.21), z_(w) { new (this) X; } // Same here
};


This looks even more promising and in fact it does everything I want it to do. Seems clever as I am using a placement new on the same object. In reality, it is an even worst solution then before. Mystery#2 of the Week is why is this worst?

In fact C++03 had no better solution which is either more cumbersome, or error prone.

C++0x will fix this for the sake of improved teachability and library building. The solution is to simply allow the call to the target constructor in the initializer list of the delegating constructor. We call the caller the delegating constructor and the callee is the target constructor

class X {  
Y y_;
Z z_;
void init();
public:
X( int i, const Widget &w): y_(i),z_(w) { init();}
X(){ init(); };
X( int i ) : X( i, 1.23) { }
X( Widget w): X(3.21, w) { }
};


The final accepted paper is in:
n1986
#cppcafe
#C/C++andFortran
#C/C++-compilers-for-AIX
#c++0x
9 comments
0 views

Permalink

Comments

Mon November 07, 2011 02:06 AM

Originally posted by: steven.zhang373


This article is translated into Chinese at: https://www.ibm.com/developerworks/mydeveloperworks/blogs/12bb75c9-dfec-42f5-8b55-b669cc56ad76/entry/c_0x__e5_a7_94_e6_89_98_e6_9e_84_e9_80_a0_e5_87_bd_e6_95_b05?lang=en

Thu December 11, 2008 11:23 PM

Originally posted by: Michael_Wong


I think I have to give it to you for the many cases you described. What I was looking for is not that far from what you were describing. It is that now in Mystery #2, we have an imbalance in the number of constructor and destructors, which is always a sign that something bad will happen. May be not immediately, but likely when you add any more complexity to the class (such as more members). Just counting the construction of X, you will see that there are 2 constructor calls, and only one destructor call. This, as you said, is the worst solution. Well done.

Thu December 04, 2008 07:19 PM

Originally posted by: hstong


I think that the standard is a bit vague on what constitutes a "reuse" of storage. This makes it hard for me to tackle the issue with object lifetimes directly. Anyhow, another case where the placement new solution above will not work is when the object of type X is actually a base class subobject itself.

Thu December 04, 2008 09:14 AM

Originally posted by: Michael_Wong


Not bad, and you are definitely dancing around the whole issue of object life times which is where the answer lies. My response to this is that there is indeed a memory leak, but it need not be from any dynamic memory allocation of subobjects (which I assume you mean embedded members Y and Z).

Wed December 03, 2008 07:38 PM

Originally posted by: hstong


The subobjects did not have their destructors called. There can be a memory leak if a subobject allocated memory dynamically.

Wed December 03, 2008 04:38 PM

Originally posted by: Michael_Wong


Even if an exception is not thrown, this code has problems ... But you are right in that an exception would be even more problematic.

Wed December 03, 2008 03:49 PM

Originally posted by: hstong


Mystery #2: Among other things, destructors for the members of X will be called twice if init() throws an exception. This is the worst solution because it has undefined behaviour.

Wed December 03, 2008 03:22 PM

Originally posted by: Michael_Wong


Good work!. You are right. The temporary will get created, and destroyed at the end of scope. No delegation.

Wed December 03, 2008 03:09 PM

Originally posted by: hstong


Mystery #1: In
X( int i ) : y_( i ),z_(1.23) { X(); }
you are constructing a temporary and not delegating to the default constructor (as intended).