[comp.lang.c++] constructor called within constructor???

noemi@sugarfoot.net.com (Me) (07/04/90)

I don't normally subscribe to this group, so if I have violated any
protocols or standards for questions, please forgive me.  Please mail
any responses directly to me, unless they are of general interest
(I can't imagine that!!).

		Help!!!!  I'm new to C++.

I'm trying to call a constructor routine (1) from withing another
constructor routine (2).  Essentially, I want constructor 2 to do
exactly what constructor 1 does, plus one extra thing:

	public:
		classname();		/* constructor 1 */
		classname(int arg);	/* constructor 2 */

	/* constructor 1 */
	classname:classname()
	{
		/* lots of things */
	}

	/* constructor 2 */
	classname:classname(int arg)
	{
		classname();		/* call constructor 1 */
		privatevar = arg;	/* "one extra thing" */
	}

Unfortunately, whatever constructor 1 does is getting lost.  That is,
private variables initialized inside constructor 1 are no longer thus
after the call (at the "one extra thing" line).

I've tried all of the following to use constructor 1, none so far have
worked (since then I've discovered that assignment to "this" is considered
obsolete and some compilers don't even allow it):

		classname();			/* constructor 1 */

		this = new classname();

		*this = classname();

		this.classname();		/* doesn't compile */

		this = this->classname();	/* doesn't compile */

		classname 	this();		/* doesn't compile */


One grody solution is to make constructor 2 an exact copy of constructor 1,
plus the extra line, but this offends my deeply ingrained committment to
modularity.  

Both constructor routines (with 0 and 1 arguments) are necessary, so adding
"arg" to constructor 1 is not an option (I can't change a whole lot anyway
since this code belongs to a different deparment; hence I must tiptoe about.)

What can I do? 

noemi
--
uucp: ames!unet!noemi
inet: noemi@sugarfoot.net.com

ark@alice.UUCP (Andrew Koenig) (07/05/90)

In article <965@unet.UUCP>, noemi@sugarfoot.net.com (Me) writes:

> I'm trying to call a constructor routine (1) from withing another
> constructor routine (2).  Essentially, I want constructor 2 to do
> exactly what constructor 1 does, plus one extra thing:
> 
> 	public:
> 		classname();		/* constructor 1 */
> 		classname(int arg);	/* constructor 2 */

> 	/* constructor 1 */
> 	classname:classname()
> 	{
> 		/* lots of things */
> 	}

> 	/* constructor 2 */
> 	classname:classname(int arg)
> 	{
> 		classname();		/* call constructor 1 */
> 		privatevar = arg;	/* "one extra thing" */
> 	}

Of course this doesn't work: the call

	classname();

means `please construct an object of type `classname' using its
empty constructor and then throw it away.'

In fact, there is no way to call a constructor explicitly (yes, I
know this statement is not completely true, but it is true for
this purpose); the way to do what you want is to reorganize your
program slightly:

	public:
		classname();		// constructor 1
		classname(int);		// constructor 2
	private:
		void build();		// do the real work


	classname::classname()		// constructor1
	{
		build();
	}

	classname::classname(int arg)	// constructor2
	{
		build();
		privatevar = arg;
	}

	void classname::build()
	{
		// the stuff that was in constructor1
	}

If you're worried about overhead, make the `build' function inline.
-- 
				--Andrew Koenig
				  ark@europa.att.com

garry@alice.UUCP (garry hodgson) (07/05/90)

In article <965@unet.UUCP>, noemi@sugarfoot.net.com (Me) writes:
> 
> I'm trying to call a constructor routine (1) from withing another
> constructor routine (2).  Essentially, I want constructor 2 to do
> exactly what constructor 1 does, plus one extra thing:
...
> Both constructor routines (with 0 and 1 arguments) are necessary, so adding
> "arg" to constructor 1 is not an option (I can't change a whole lot anyway
> since this code belongs to a different deparment; hence I must tiptoe about.)
> 
> What can I do? 

A habit I've gotten into is one I picked up from looking at InterViews code.
That is, every class has an Init() member, which does all the "normal"
initialization for the class.  Each constructor then calls Init().
This solves the problem nicely.

void
Foo::Init()
{
	do normal init stuff here
}

Foo::Foo()
{
	Init();
}

Foo::Foo( char *name )
{
	Init();
	fileName = name;
}

garry hodgson

jimad@microsoft.UUCP (Jim ADCOCK) (07/06/90)

In article <965@unet.UUCP> noemi@sugarfoot.net.com (Me) writes:
>I'm trying to call a constructor routine (1) from withing another
>constructor routine (2).  Essentially, I want constructor 2 to do
>exactly what constructor 1 does, plus one extra thing:
>
>	public:
>		classname();		/* constructor 1 */
>		classname(int arg);	/* constructor 2 */
>
>	/* constructor 1 */
>	classname:classname()
>	{
>		/* lots of things */
>	}
>
>	/* constructor 2 */
>	classname:classname(int arg)
>	{
>		classname();		/* call constructor 1 */
>		privatevar = arg;	/* "one extra thing" */
>	}
>
>Unfortunately, whatever constructor 1 does is getting lost.  That is,
>private variables initialized inside constructor 1 are no longer thus
>after the call (at the "one extra thing" line).

This is a common mistake when first exposed to C++ -- and is covered in 
ARM [though I can't find the page.]  In C++ saying:

	classname();

*always* means construct a new object of type classname.  If you don't
specify a name for that object, you get a new anonymous object.  So,
when you said "classname();" in classname::classname(int arg) you just
created an additional, temporary, unnamed object on the stack.  Why does
the language do this? -- Because in C++ constructors can also be used
as conversion routines, consider:

	complex c1, c2;
	double d;

	...

	c1 = c2 + complex(d);
		  ^ anonymous complex constructed.

How does one factor out common code in constructors?  -- Following the
example in ARM, one writes a helper routine, say called init(), which is
then called from each constructor.  If that still doesn't seem pleasing
enough, consider that one can introduce an intermediate class whose 
constructor takes no parameters, and performs the common initialization 
portion of the final class's construction.  Then the final derived class's 
constructors can take any parameters, and perform the variant portion of the 
construction effort. 

roger@procase.UUCP (Roger H. Scott) (07/11/90)

Andrew Koenig's "helper" functions [build()] don't solve the problem of
factoring out common *initialization*.  For example,

    class Base {
    public:
	Base(int, double, char *, char *);
	...
    };

    class String {
    public:
	String(char *);
	...
    };

    class Derived : public Base {
    public:
	Derived() : Base(2, 4.6, "foo", "bar"), Mem1("aack"), Mem2("pfft"),
	    myI(0), myJ(0) {
	}
	Derived(int i) : Base(2, 4.6, "foo", "bar"), Mem1("aack"), Mem2("pfft"),
	    myI(i), myJ(0) {
	}
	Derived(int i, int j):Base(2,4.6,"foo","bar"),Mem1("aack"),Mem2("pfft"),
	    myI(i), myJ(j) {
	}

    private:
	String Mem1, Mem2;
	int myI, myJ;
    };

A long time ago I asked the AT&T folks to define

    class Foo {
    public:
	Foo();
	Foo(int i) : Foo(), myI(i) {}
    };

to mean "to construct a Foo object from an integer, construct a Foo object
using the default Foo constructor and then execute the remainder of the
Foo(int) constructor body".  This is by analogy with "Derived() : Base() {}".
Needless to say, my idea was not adopted.

jimad@microsoft.UUCP (Jim ADCOCK) (07/17/90)

In article <174@logo.procase.UUCP> roger@procase.UUCP (Roger H. Scott) writes:
>Andrew Koenig's "helper" functions [build()] don't solve the problem of
>factoring out common *initialization*.  For example,
....

Again, an alternative approach to constructor helper methods is to introduce 
an intermediate helper class.  C++ is designed such that introducing a helper 
class need not lead to an unreasonable cost.

    class Base {
    public:
	Base(int, double, char *, char *);
    };

    class String {
    public:
	String(char *);
    };

    class HelpDerive : public Base {
    public:
	HelpDerive() : Base(2,4.6,"foo","bar"), Mem1("aack"), Mem2("pfft") { }
    private:
	String Mem1, Mem2;
    };

    class Derived : public HelpDerive {
    public:
	Derived() : myI(0), myJ(0) { }	    // [this example could be handled 
	Derived(int i) : myI(i), myJ(0) { } // better via default args ....
	Derived(int i, int j) : myI(i), myJ(j) { } // .... ]
    private:
	int myI, myJ;
    };