Topic: C++ constructors
Author: Ravikant Iyer <bom@slip.net>
Date: 1997/05/29 Raw View
In article <Pine.LNX.3.95.970528190556.302B-100000@balabit.saturnus.vein.hu>,
Balazs Scheidler <bazsi@tas.vein.hu> wrote:
>
> Hello there,
>
>I've a small question about C++ constructors, and their braindamaged
>behaviour. I programmed in OOP in Pascal before, and I'm using C++ now.
>My problem lies in the fact that a constructor unconditionally sets the
>VMT (or whatever the virtual method table is called in c++) pointer upon
>startup. So when a constructor calls a virtual method, it always calls
>it at the same level the constructor runs on. What I mean exactly?
>
Your problem has nothing to do with how virtual functions are implemented by a
particular compiler.. neither does C++ endorse/put down any implementation
details for virtual functions.. Compilers behave so because the language
defines specific rules for virtual functions being called from constructors
and destructors.
[e.g. snipped]
The reason for this is that the derived class is not yet constructed when the
base class constructor is running.. Calling member functions on objects that
are not fully constructed is to say the least dangerous..
consider..
struct Base
{
Base() { foo ();}
virtual void foo() { /*stuff*/ }
};
struct Derived : Base
{
Derived()
{
fooptr = new FooType;
}
void foo () { fooptr->fooSomething(); } // BOOM
private:
TooType fooptr;
};
now obviously if C++ worked as u suggested we would have a disaster at this
point. To prevent such cases the language allows calls to virtual functions
from constructors/destructors but says that they will resolve to a function
defined either in the class for which the constructor is being executed or
in one of its base classes.
>If I instantiated an object from MyApp, the Menu member variable would
>never be initialized, since MyApp ctor calls Application ctor which sets
>VMT to point to its own VMT -> InitMenuBar is called at the Application
>level. This can only be avoided by using an additional Initialize member
>function for Application (preferable virtual), and call this function
>after new'ing the new object instance.
>
>App = new MyApp();
>App->Initialize(); // calls InitMenuBar, and at this point VMTs are set
> up properly
>
The way to do this would be to call the InitMenuBar member function from the
constructor of the derived class. This function _need_ not be virtual but can
be if required for some other reason.
>destructors are working as expected.
>
If you mean the destructor calls the function in your derived class then that
is either a compiler bug, or you probably forgot to make the destructor of
your base class as virtual.. The result of doing so is undefined as per the
C++ draft standard.
>My other question is about the automated calling of inherited
>constructors. Why am I not given the opportunity of doing something useful
>before calling an inherited constructor?
For the same reason as before.. You see it is perfectly legal and a normal
practice to call member functions inherited from your base classes in the
constructor. Calling member functions on un constructed objects is undefined
and hence the base class constructor is always called first.
>This prevents me doing anything
>useful in constructors and allows using them only as a means of parameter
>passing.
It dose not prevent you from doing any thing.. I cannot see how the base
class' constructor running causes the derived class any problems. unless you
are thinking of doing something in the derived class that the base class
constructor *needs* to know and behave accordingly.. That IMHO is very bad
design.
>Ok, ok you could say that they ARE for parameter passing only.
>But what is if I need a parameter only during class initialization? I'd
>have to store this parameter in a member and would never be used except
>upon startup.
You are free to do what ever with the parameters passed to the constructor..
no need to store them in member variables or any such a thing :-)
>And another thing is error handling. There's always a
>possibility to get an error even in a constructor. As a C++ programmer I'd
>like to handle this error using exceptions. I'd like to do something like
>this:
>
>class Object : public Base {
>public:
> Object();
>};
>
>Object::Object()
>{
> try {
> Base::Base();
> }
> catch (...) {
> cout << "Error occured." << endl;
> }
>}
>
>But a default constructor of the base class is always called. Of course
>the additional Initialize() member function would allow these kind of
>error checks, but I don't like that. (there's still the question of
>parameter passing I mentioned above).
>
use a function try block for this..
here's how
Object::Object() try // any base class or member init
{
/// ... code in here
}
catch (...)
{
// handle the errors here and rethrow.
}
In this case the catch will catch all exceptions thrown during base class
construction and member initialization..
cheers
-bom
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "Ian Griffiths" <ian.griffiths@dmv.co.uk.no.spams.please>
Date: 1997/05/29 Raw View
Balazs Scheidler <bazsi@tas.vein.hu> wrote:
> My other question is about the automated calling of inherited
> constructors. Why am I not given the opportunity of doing something
> useful before calling an inherited constructor?
If your inherited constructor takes a parameter, could you not do this:
Spong::Spong (int i) : BaseSpong (doThis(), doThat(), doTheOther(), i) {}
(where Spong is derived from BaseSpong and both have constructors which
take an int)?
I am assuming that it would be illegal to do the following though:
Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), ) {}
or even
Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), (void)) {}
This looks pretty grim to me. Does it actually work?
Presumably this can be a problem if doThis() etc. are member functions of
Spong because this is an implicit use of 'this' in the constructor
initialisation list, which is not a good idea.
> This prevents me doing anything useful in constructors and
> allows using them only as a means of parameter passing.
It doesn't prevent you doing anything useful in constructors, it just
prevents you doing anything useful before you bases' and members'
constructors have run.
What were you planning to do before they run? If you want to change
anything internal to the class, surely it can wait: you could only think
of altering POD types because none of your other entities have been
constructed, but one of two cases would apply: either changes you could
make before other ctors are run would be overwritten because these ctors
will be working on the assumption that the data is uninitialised before
they run, or your changes will not get used because the ctors for some
reason decided they didn't need to initialise the data and therefore
certainly shouldn't be trying to use it, and so it won't make any
difference if you only initialise it after they've run. (And if you're
initialising something specific to your derived version of a base class,
then it won't be able to see anything you've done because the base class
doesn't know it's destined to be a derived type.)
Actually brings to mind a question. Is a derived type allowed to
initialise base members? E.g.
class A { public: int aval; }
class B : public A
{
public:
B::B () : aval(3) {}
};
and if so, what happens if all the derived classes try to initialise it
with something different?
> And another thing is error handling. There's always a
> possibility to get an error even in a constructor. As a C++ programmer
I'd
> like to handle this error using exceptions. I'd like to do something like
> this:
>
> class Object : public Base {
> public:
> Object();
> };
>
> Object::Object()
> {
> try {
> Base::Base();
> }
> catch (...) {
> cout << "Error occured." << endl;
> }
> }
Exceptions thrown in construction mean that construction has failed. If a
base constructor fails, your derived object is doomed never to be created,
so running its constructor doesn't make sense. If you could what you've
done then this allows you (as you have done) to swallow the exception.
This means that the following could happen:
Object newObj; // Base ctor throws, but Object ctor swallows it
so what is newObj now? Something which failed construction, but didn't
throw an exception to tell us. So we have an automatic variable which is
in scope but not valid because it failed construction. What can we do with
that?
--
Ian Griffiths
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "John Hickin" <hickin@nortel.ca>
Date: 1997/05/29 Raw View
This is standard for C++. At times it can be a bit annoying, especially if you
are used to the Smalltalk type of object model.
In your example this behavior can actually be construed as beneficial. Often
your C++ class will wrap an underlying 'widget'; just rename your initialize()
virtual to display() and you'll get a feel for the benefits (i.e., widgets not
instantiated until you need to display the window).
--
John Hickin Nortel Technology, Montreal, Quebec
(514) 765-7924 hickin@nortel.ca
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/05/29 Raw View
"Ian Griffiths" <ian.griffiths@dmv.co.uk.no.spams.please> writes:
>Balazs Scheidler <bazsi@tas.vein.hu> wrote:
>> My other question is about the automated calling of inherited
>> constructors. Why am I not given the opportunity of doing something
>> useful before calling an inherited constructor?
>
>If your inherited constructor takes a parameter, could you not do this:
>
>Spong::Spong (int i) : BaseSpong (doThis(), doThat(), doTheOther(), i) {}
>
>(where Spong is derived from BaseSpong and both have constructors which
>take an int)?
You need an extra set of parentheses:
Spong::Spong (int i) : BaseSpong ((doThis(), doThat(), doTheOther(), i)) {}
^ ^
>I am assuming that it would be illegal to do the following though:
>
>Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), ) {}
>or even
>Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), (void)) {}
>
>This looks pretty grim to me. Does it actually work?
Nope. But you could always add an empty base class for this purpose:
class Dummy {
Dummy(int) {}
}
class Spong : private Dummy, public BaseSpong { ... }
Spong::Spong () :
Dummy((doThis(), doThat(), doTheOther()),
BaseSpong()
{}
>Presumably this can be a problem if doThis() etc. are member functions of
>Spong because this is an implicit use of 'this' in the constructor
>initialisation list, which is not a good idea.
Yes, you have to be careful.
>Actually brings to mind a question. Is a derived type allowed to
>initialise base members?
I don't think so. (Sorry, I don't have chapter and verse for that one.)
>> class Object : public Base {
>> public:
>> Object();
>> };
>>
>> Object::Object()
>> {
>> try {
>> Base::Base();
>> }
>> catch (...) {
>> cout << "Error occured." << endl;
>> }
>> }
You got the syntax wrong. But I think you can do this sort of thing
with function try blocks.
...
>... this allows you ... to swallow the exception.
Well, in the case of function try blocks, it doesn't.
The exception will be implicitly rethrown.
| 15.3 Handling an exception [except.handle]
|
| 16 The exception being handled is rethrown if control reaches the end of
| a handler of the function-try-block of a constructor or destructor.
--
Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3 | -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: dat93ser@ludat.lth.se (Stefan Eriksson)
Date: 1997/05/30 Raw View
On 28 May 97 17:55:01 GMT, Balazs Scheidler <bazsi@tas.vein.hu> wrote:
>I've a small question about C++ constructors, and their braindamaged
>behaviour. I programmed in OOP in Pascal before, and I'm using C++ now.
>My problem lies in the fact that a constructor unconditionally sets the
>VMT (or whatever the virtual method table is called in c++) pointer upon
>startup. So when a constructor calls a virtual method, it always calls
>it at the same level the constructor runs on. What I mean exactly?
There has been some discussion about this matter earlier in this
newsgroup. The problem is that constructor will only call virtual
instances of methods that are defined in the same type as the
constructor itself or in any supertype. The reason is that the
constructors are run from supertype to subtype, so the subtype is not
initialized when the constructor is run.
>My other question is about the automated calling of inherited
>constructors. Why am I not given the opportunity of doing something useful
>before calling an inherited constructor? This prevents me doing anything
>useful in constructors and allows using them only as a means of parameter
>passing. Ok, ok you could say that they ARE for parameter passing only.
>But what is if I need a parameter only during class initialization? I'd
>have to store this parameter in a member and would never be used except
>upon startup. And another thing is error handling. There's always a
>possibility to get an error even in a constructor. As a C++ programmer I'd
>like to handle this error using exceptions. I'd like to do something like
>this:
>
>class Object : public Base {
>public:
> Object();
>};
>
>Object::Object()
>{
> try {
> Base::Base();
> }
> catch (...) {
> cout << "Error occured." << endl;
> }
>}
It should look like this:
(example from http://www.maths.warwick.ac.uk/c++/pub/wp/html/cd2/
search for 'function-try-block')
[Example:
int f(int);
class C {
int i;
double d;
public:
C(int, double);
};
C::C(int ii, double id)
try
: i(f(ii)), d(id)
{
// constructor function body
}
catch (...)
{
// handles exceptions thrown from the ctor-initializer
// and from the constructor function body
}
--end example]
//Stefan
-- ___________________________________________________________________
__w__ Stefan Eriksson kamnarsvagen 13:C108 226 46 LUND, tel: 046-393668
o.o Mail to dat93ser@ludat.lth.se, stefer@df.lth.se
_m__~__m__HomePage_________http://www.ludat.lth.se/~dat93ser_________________
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/05/30 Raw View
On 29 May 97 12:28:37 GMT, Ian Griffiths wrote:
: If your inherited constructor takes a parameter, could you not do this:
: Spong::Spong (int i) : BaseSpong (doThis(), doThat(), doTheOther(), i) {}
No, that would be a constructor with four parameters. But
: BaseSpong ((doThis(), doThat(), doTheOther(), i))
would work.
: (where Spong is derived from BaseSpong and both have constructors which
: take an int)?
: I am assuming that it would be illegal to do the following though:
: Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), ) {}
: or even
: Spong::Spong () : BaseSpong (doThis(), doThat, doTheOther(), (void)) {}
Hum
: BaseSpong(static_cast<void>(doThis(), doThat, doTheOther()))
: This looks pretty grim to me. Does it actually work?
No, not today. Maybe in August. Seems like there is a proposal to
allow sending a void expression to an empty parameter list.
[ other sane comments snipped ]
John
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Gerard Weatherby <gerardw@alum.mit.edu>
Date: 1997/06/02 Raw View
Balazs Scheidler wrote:
-
- class Application {
- public:
- Application();
- virtual void InitMenuBar();
- protected:
- MenuBar *Menu;
- };
-
- Application::Application()
- {
- InitMenuBar();
- }
-
- void Application::InitMenuBar()
- {
- Menu = NULL;
- }
-
- class MyApp : public Application {
- public:
- MyApp() : Application() {};
- virtual void InitMenuBar();
- };
-
- virtual MyApp::InitMenuBar()
- {
- Menu = new MenuBar(...);
- }
-
- If I instantiated an object from MyApp, the Menu member variable would
- never be initialized, since MyApp ctor calls Application ctor which
sets
- VMT to point to its own VMT -> InitMenuBar is called at the
Application
- level. This can only be avoided by using an additional Initialize
member
- function for Application (preferable virtual), and call this function
- after new'ing the new object instance.
You can just do this:
class Application {
public:
Application( ):Menu(0){}
protected:
Application(Menu *m):Menu(m){}
MenuBar *Menu;
};
class MyApp : public Application {
public:
MyApp() : Application(new MenuBar(...) ) {};
};
- I was trying to read the C++ standard as well but at first sight I
- didn't understand much of it
The draft standard is not a good way to get the basics. Stroustrup's
books
(referenced in the FAQ's) address the questions you're asking.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Balazs Scheidler <bazsi@tas.vein.hu>
Date: 1997/05/28 Raw View
Hello there,
I've a small question about C++ constructors, and their braindamaged
behaviour. I programmed in OOP in Pascal before, and I'm using C++ now.
My problem lies in the fact that a constructor unconditionally sets the
VMT (or whatever the virtual method table is called in c++) pointer upon
startup. So when a constructor calls a virtual method, it always calls
it at the same level the constructor runs on. What I mean exactly?
class Application {
public:
Application();
virtual void InitMenuBar();
protected:
MenuBar *Menu;
};
Application::Application()
{
InitMenuBar();
}
void Application::InitMenuBar()
{
Menu = NULL;
}
class MyApp : public Application {
public:
MyApp() : Application() {};
virtual void InitMenuBar();
};
virtual MyApp::InitMenuBar()
{
Menu = new MenuBar(...);
}
If I instantiated an object from MyApp, the Menu member variable would
never be initialized, since MyApp ctor calls Application ctor which sets
VMT to point to its own VMT -> InitMenuBar is called at the Application
level. This can only be avoided by using an additional Initialize member
function for Application (preferable virtual), and call this function
after new'ing the new object instance.
App = new MyApp();
App->Initialize(); // calls InitMenuBar, and at this point VMTs are set
up properly
destructors are working as expected.
My other question is about the automated calling of inherited
constructors. Why am I not given the opportunity of doing something useful
before calling an inherited constructor? This prevents me doing anything
useful in constructors and allows using them only as a means of parameter
passing. Ok, ok you could say that they ARE for parameter passing only.
But what is if I need a parameter only during class initialization? I'd
have to store this parameter in a member and would never be used except
upon startup. And another thing is error handling. There's always a
possibility to get an error even in a constructor. As a C++ programmer I'd
like to handle this error using exceptions. I'd like to do something like
this:
class Object : public Base {
public:
Object();
};
Object::Object()
{
try {
Base::Base();
}
catch (...) {
cout << "Error occured." << endl;
}
}
But a default constructor of the base class is always called. Of course
the additional Initialize() member function would allow these kind of
error checks, but I don't like that. (there's still the question of
parameter passing I mentioned above).
I'm opened to any solutions to the above problems (I hope you could
understand my English ;-). I was trying to read the C++ standard as well
but at first sight I didn't understand much of it ;-) Maybe I've missed
something.
Best regards,
Balazs Scheidler
*-------------------------------------------------------------------------*
| Author of Turbo Vision Resource WorkShop, the ultimate resource toolkit |
| for Turbo Vision for Pascal and C++. You can get the latest version at |
| http://www.bitsoft.com/tvrw |
+-------------------------------------------------------------------------+
| UNIX is user friendly. It's just selective about who its friends are. |
*--------------------------------------------*bAzSi*----------------------*
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: Steve Clamage <stephen.clamage@Eng.Sun.COM>
Date: 1997/05/28 Raw View
Balazs Scheidler wrote:
>
> I've a small question about C++ constructors, and their braindamaged
> behaviour. I programmed in OOP in Pascal before, and I'm using C++ now.
> My problem lies in the fact that a constructor unconditionally sets the
> VMT (or whatever the virtual method table is called in c++) pointer upon
> startup. So when a constructor calls a virtual method, it always calls
> it at the same level the constructor runs on. ...
>
> ... destructors are working as expected.
>
> My other question is about the automated calling of inherited
> constructors. Why am I not given the opportunity of doing something useful
> before calling an inherited constructor?
The deliberate design decision for C++ is that classes are
initialized automatically from most-base class to most-derived
(final) class. At each stage, the object has the type of the
constructor currently running, and at each stage the object is
a fully-initialized object of the current type. It is therefore
impossible to access a subobject in an indeterminate state, and
the virtual function mechanism will never invoke a derived-class
version until the derived class has been initialized.
Thus, the behavior of an object is fully consistent with the
definition of its sub-objects at all times -- during construction,
after construction, and during destruction.
(I'm assuming in this discussion that the defined initialization
of an object is correct.)
Yes, this means you have less flexibility in the creation and
destruction of objects. It also means that object initialization
is always automatic and consistent, and you and clients of your
classes have less to remember about initialization and destruction.
If everyone is always required to call init and destroy functions
everywhere, they will be forgotten or called out of order somewhere,
bugs which can be very hard to find. (The only reason you would want
to change the default order is because you are doing something
non-obvious, and that in turn means people who derive from your
class are likely to get it wrong.)
Other languages have other designs, and you are free to disagree with
the C++ design. This behavior is quite fundamental to C++ and is
unlikely ever to change.
BTW, I find it strange that you don't like the way constructors work,
but think that destructors work properly. They are completely
complementary, so I would think that any argument in favor of making
construction more flexible would apply equally to destruction!
--
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]