[comp.lang.c++] constructor calls constructor?

kc@rna.UUCP (Kaare Christian) (05/26/89)

A question, if you please.

I have a class that has several constructors.
	class X {
		...
	public:
		X();
		X(int n);

All of these constructors have some mundane chores that must be accomplished,
plus there are extra chores for those that take extra arguments. What I tried
to do was have the "parameter taking" constructors all call the
parameterless constructor. So, I had something like
	X::X(int n)
	{
		X();
		// the extra work
	}
This didn't work, the call to X() created a new object, it didn't
initialize the current object. (I guess this makes sense, 'cause a
statement like A=X() should create a new object.) Then I tried
	X::X(int n)
	{
		this->X();
		// the extra work here
	}
But this didn't pass muster, Zortech C++ complained that "X wasn't a member
of class X."

Is there a way for one constructor to call another, simply to have
some chores performed, while avoiding real construction? (I know I can
have all the constructors call some helper function, but that
seems too roundabout.)

Thanks,
Kaare Christian

ark@alice.UUCP (Andrew Koenig) (05/27/89)

In article <601@rna.UUCP>, kc@rna.UUCP (Kaare Christian) writes:

> Is there a way for one constructor to call another, simply to have
> some chores performed, while avoiding real construction?

Nope.  Roundabout or not, the best way to do it is with
helper functions.

However, if you really insist, C++ 2.0 will have a way to say
`construct an object of type T at location X," as follows:

	new (X) T (args);

So by saying

	new (this) T (args);

you will, in effect, be able to call one constructor from another.
Having called this operator new, you'll need to define it:

	void* operator new(size_t,T* tp) { return tp; }

and then you're in business.

I said `if you really insist' because I think it's conceptually
cleaner to use helper functions and it is probably worthwhile
not to cut yourself off from older C++ implementations.
-- 
				--Andrew Koenig
				  ark@europa.att.com

sarima@gryphon.COM (Stan Friesen) (05/28/89)

In article <601@rna.UUCP> kc@rna.UUCP (Kaare Christian) writes:
>A question, if you please.
>
>I have a class that has several constructors.
>[Examples deleted]
>All of these constructors have some mundane chores that must be accomplished,
>plus there are extra chores for those that take extra arguments. What I tried
>to do was have the "parameter taking" constructors all call the
>parameterless constructor.
>
>This didn't work, the call to X() created a new object, it didn't
>initialize the current object. 
>
>>[Other failed approach deleted]
>Is there a way for one constructor to call another, simply to have
>some chores performed, while avoiding real construction? (I know I can
>have all the constructors call some helper function, but that
>seems too roundabout.)
>
	What's so round-about in using a helper function?   Especially
since the helper can be made a private function, used only in constructors?
It is also quite readable.
-- 
Sarima Cardolandion			sarima@gryphon.CTS.COM
aka Stanley Friesen			rutgers!marque!gryphon!sarima
					Sherman Oaks, CA

shopiro@alice.UUCP (Jonathan Shopiro) (05/30/89)

In article <9396@alice.UUCP>, ark@alice.UUCP (Andrew Koenig) writes:
> In article <601@rna.UUCP>, kc@rna.UUCP (Kaare Christian) writes:
> 
> > Is there a way for one constructor to call another, simply to have
> > some chores performed, while avoiding real construction?
> 
> Nope.  Roundabout or not, the best way to do it is with
> helper functions.
> 
> However, if you really insist, C++ 2.0 will have a way to say
> `construct an object of type T at location X," as follows:
> 
> 	new (X) T (args);
> 
> So by saying
> 
> 	new (this) T (args);
> 
This approach should be avoided.  If you use it, you will initialize an
object twice.  For example

	struct M {
		M();
	};
	struct S {
		M	mem;
		S();
		S(int);
	};
	S::S(int)
	{
		new (this) S;	// take care of mundane chores
		// other less mundane chores
	}
	// ...
	S	s(3);

When the object s is initialized, its member s.mem will be initialized
twice because each time a constructor of class S is invoked, all
members and bases of S are initialized by compiler-synthesized code.
It is fundamental to the C++ notion of object, that each object is
initialized once when it comes into existence and destroyed once when
it disappears.  In practice, surprisingly many classes depend on this
invariant and their constructors will misbehave if they are invoked
twice on the same object.  The new ``placement'' syntax that Andy
mentioned was introduced to permit objects to be initialized in a
user-specified location (e.g., in shared memory), rather than to
support invocation of constructors on already-initialized objects.
The fact that the latter is not prohibited is unfortunate.

An alternative approach to Kaare's problem would be to rely on
compiler-synthesized code to do the ``mundane chores.''  The most
extreme example of this would be to create a new class publicly
derived from the original class and move the fancy constructors to the
new class.  The original class should be renamed and its original name
given to the new class.  For example,

	class Old_X {
	friend X;
		Old_X();
		// all the private members from the original X
	public:
		// all the public members of the original X, save constructors
	};
	class X : public Old_X {
	public:
		X() {}	// the no-arg constructor just invokes
			// the one from the base class
		X(int n);
		// nothing else
	};
	Old_X::Old_X()
	{
		// mundane chores here
	}
	X::X(int n)
	{	// Old_X::Old_X() is invoked automagically
		// the extra work
	}

Note in the above that since the constructor of Old_X is private, it
can't be instantiated by itself, but only as a base of an X object.
Also, the program is more reliable because the Old_X constructor is
only invoked by compiler-generated code.  The user is supposed to
ignore the existence of Old_X and just use X.  You can discourage
the user from trying to access Old_X apart from X by giving it a
very long name.

It is often the case that you don't need to go to the trouble of defining
an extra class for this problem.  Perhaps the initializers of the bases
and members that are already there are sufficient.  Or maybe a helper
function is the best choice after all!  :-)

-- 
		Jonathan Shopiro
		AT&T Bell Laboratories, Warren, NJ  07060-0908
		research!shopiro   (201) 580-4229