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; };