Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Really want virt func in ctor

0 views
Skip to first unread message

Bengt Cyren

unread,
Mar 23, 2000, 3:00:00 AM3/23/00
to
Fellow developers. For a beginner, the fact that virtual functions
doesn't work "as expected" in base ctors may come as a surprise. I've
been aware of this pitfall for quite some time now. Still, there is a
situation where I really would like the base ctor to use the vtable from
the descendant. My compiler doesn't have RTTI nor templates, but I do
have a RTTI system of my own design (for some classes). Of course all
type info exists regardless whether there are any instances of a
particular class. Here's a simplified illustration:

class ANIMAL
{
public:
ANIMAL() { /* Can't use pType(), but really needs to */ };
virtual char* pType() const = 0;
};

class DOG : public ANIMAL
{
public:
virtual char* pType() const { return "Dog"; };
};

It would be perfectly safe for the ANIMAL::ANIMAL() to call DOG::pType()
virtually, even though DOG isn't created yet. I know this is true in
every descendant, because both the declaration and definition of pType()
is created by my own RTTI macros.

The only way I can think of to get the RTTI down to the base ctor is
through ctor arguments. I really don't want to do that. It makes the
code harder to read and it also increase both size and execution time.
My question is; Does anybody have a clever solution to this problem?

Thanks in advance. /Bengt Cyren

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]


Steve Clamage

unread,
Mar 24, 2000, 3:00:00 AM3/24/00
to
Bengt Cyren wrote:
>
> Fellow developers. For a beginner, the fact that virtual functions
> doesn't work "as expected" in base ctors may come as a surprise. I've
> been aware of this pitfall for quite some time now.

It's not a pitfall. It is fundamental to the design of C++.

> Still, there is a
> situation where I really would like the base ctor to use the vtable from
> the descendant.

You cannot do that in C++, at least there is no C++ code having
defined behavior that will accomplish it.

A fundamental principle of C++ is that you have an object only during
the interval after the constructor has completed and before the
destructor starts to run. During construction (or destruction), the
type of the object is the type of the constructor's (or destructor's)
own class.

The reason for that principle is that you should not want to call a
derived-class function if the derived portion of the object hasn't
been initialized yet, or has already been destroyed.

Other languages, like Java, have different different principles.
The C++ principle ensures type safety. You have to go out of your
way, or use bad programming practices, to run a member function
on an object that doesn't exist.

You need to think about your problem another way.

In your example, you wanted to call a virtual function in a derived
class from the base-class constructor to return some data specific
to the derived class.

Why does the base-class constructor need derived-class data?
That violates any abstraction the classes are implementing.
If the base class needs some data, pass it to the constructor.

--
Steve Clamage, stephen...@sun.com

Runu Knips

unread,
Mar 24, 2000, 3:00:00 AM3/24/00
to
Bengt Cyren schrieb:
> [...] For a beginner, the fact that virtual functions

> doesn't work "as expected" in base ctors may come as a surprise.

Oooooooooooooooold problem (isn't that a FAQ?).

For example, use a seperate function Create() instead of the
ctor. It may also have a return type then.

Or create an special function Init() which has to be called
by every derived subclass.

Or just try to solve the problems you have in some different
way, without such "I need to know the concrete type of the
object at ctor time" implication.

Or use Java, it doesn't behave like that (which is AFAIK
introducing other problems and possible subtle bugs). Well,
not a serious advice for this is a C++ newsgroup ;-)))

> [...] The only way I can think of to get the RTTI down to the


> base ctor is through ctor arguments.

I don't understand this statement, what did you wanted to say ???
RTTI is "RunTime Type Information" which is not working in the
ctor anyway...

> I really don't want to do that. It makes the code harder to
> read and it also increase both size and execution time.

You still can use inlining. Doesn't reduce time but makes
code of course run faster.

Andrei Alexandrescu

unread,
Mar 25, 2000, 3:00:00 AM3/25/00
to
Steve Clamage <stephen...@sun.com> wrote in message
news:38DA6E16...@sun.com...
[snip]

While I definitely agree with your nice reasoning, I think post-constructors
are conceptually sound.

Imagine you had a keyword that says "postconstruct":

class A
{
A() {}
virtual void Build() postconstruct;
}

Now every time you say:

A a;

the compiler generates the "normal C++" code:

A a;
a.Build();

When you say:

new A;

similar things happen.

In an inheritance hierarchy, Build() will be called after the complete
object has been created.

I forgot why this is more useful than overriding A::A() in derived classes
:o). Ah, I remembered: this gives the base class a chance to do something
with a valid derived object.


Andrei

Gene Bushuyev

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to
"Steve Clamage" <stephen...@sun.com> wrote in message
news:38DA6E16...@sun.com...
[snip]
> A fundamental principle of C++ is that you have an object only during
> the interval after the constructor has completed and before the
> destructor starts to run. During construction (or destruction), the
> type of the object is the type of the constructor's (or destructor's)
> own class.

I don't see how it is FUNDAMENTAL principle. It is certainly not
self-evident, and other languages apparently do not consder this principle
worthy of acceptance.

>
> The reason for that principle is that you should not want to call a
> derived-class function if the derived portion of the object hasn't
> been initialized yet, or has already been destroyed.

This is not a reason. You tell me what I should and should not want. This is
a dictatorship :-)

> Why does the base-class constructor need derived-class data?
> That violates any abstraction the classes are implementing.
> If the base class needs some data, pass it to the constructor.

As it was pointed many times during the previous discussions of this topic,
it's not a question about different data, it's a question about different
behavior. Here is an example:
class A
{
protected:
int a,b,c;
public:
A():a(0),b(1){setup();}
virtual setup(){c=a+b;}
};
class B: public A
{
public:
B(){}
virtual setup(){c=a*b;}
};

One may say that C++ provides additional protection from somebody trying to
shoot himself in a foot. Quite strange position for a language that was
deliberately designed very liberal and allowing sundry ways of screwing
things up. But here it took from me my fundamental right to call real
virtual function from a constructor :-)
But does the language actually has any protection from misdemeanor in a
constructor? Not really. All one need to do is to add a template constructor
to achieve what he/she wanted in the beginning:

class A
{
public:
int a,b,c;
public:
A(){setup(this);}
template<class T> A(T* t):a(0),b(1){T::setup(t);}
static void setup(A* t){t->c=t->a+t->b;}
};
class B: public A
{
public:
B():A(this){}
template<class T>B(T* t):A(t){}
static void setup(A* t){t->c=t->a*t->b;}
};

--
Gene Bushuyev
*** If Linux is the answer, then what is the question? ***

Steve Clamage

unread,
Mar 26, 2000, 3:00:00 AM3/26/00
to
Gene Bushuyev wrote:
>
> "Steve Clamage" <stephen...@sun.com> wrote in message
> news:38DA6E16...@sun.com...
> [snip]
> > A fundamental principle of C++ is that you have an object only during
> > the interval after the constructor has completed and before the
> > destructor starts to run. During construction (or destruction), the
> > type of the object is the type of the constructor's (or destructor's)
> > own class.
>
> I don't see how it is FUNDAMENTAL principle. It is certainly not
> self-evident, and other languages apparently do not consder this principle
> worthy of acceptance.

I did not say it was an axiom, and I didn't say it was a fundamental
principle of language design. I said it was a fundamental principle
of C++, its purpose to support type safety. I also said that other
langauges choose different design criteria.

--
Steve Clamage, stephen...@sun.com

Lornis Van Loon

unread,
Apr 4, 2000, 3:00:00 AM4/4/00
to
how about the, following - since a virtual function is really only a
pointer to a member function, just use a pointer to a member function
instead, and pass from the most derived to the base:

#include <assert.h>

enum EType
{
eBase,
eDerived
};

struct base
{
EType m_eType;

typedef void ( base:: * _TyMFnPtrInit )();

base( _TyMFnPtrInit _pmfnInit = &base::init )
{
(this->*_pmfnInit)();
}

void init()
{
m_eType = eBase;
}
};

struct derived : public base
{
derived()
// Pass pointer to derived::init() initialization method.
// In some cases a static_cast< _TyMFnPtrInit >() is necessary.
: base( &derived::init )
{ }

void init()
{
m_eType = eDerived;
}
};

int
main()
{
base b;
derived d;
assert( eBase == b.m_eType );
assert( eDerived == d.m_eType );
}

rock on, Lornis Van Loon

On 23 Mar 2000 10:30:24 -0500, cy...@my-deja.com (Bengt Cyren) wrote:

>Fellow developers. For a beginner, the fact that virtual functions
>doesn't work "as expected" in base ctors may come as a surprise. I've

>been aware of this pitfall for quite some time now. Still, there is a


>situation where I really would like the base ctor to use the vtable from

>the descendant. My compiler doesn't have RTTI nor templates, but I do
>have a RTTI system of my own design (for some classes). Of course all
>type info exists regardless whether there are any instances of a
>particular class. Here's a simplified illustration:
>
> class ANIMAL
> {
> public:
> ANIMAL() { /* Can't use pType(), but really needs to */ };
> virtual char* pType() const = 0;
> };
>
> class DOG : public ANIMAL
> {
> public:
> virtual char* pType() const { return "Dog"; };
> };
>
>It would be perfectly safe for the ANIMAL::ANIMAL() to call DOG::pType()
>virtually, even though DOG isn't created yet. I know this is true in
>every descendant, because both the declaration and definition of pType()
>is created by my own RTTI macros.
>

>The only way I can think of to get the RTTI down to the base ctor is

>through ctor arguments. I really don't want to do that. It makes the


>code harder to read and it also increase both size and execution time.

>My question is; Does anybody have a clever solution to this problem?
>
>Thanks in advance. /Bengt Cyren
>

Jim Hyslop

unread,
Apr 4, 2000, 3:00:00 AM4/4/00
to
In article <38e98697.44727167@news>,

lorn...@hotmail.com (Lornis Van Loon) wrote:
> how about the, following - since a virtual function is really only a
> pointer to a member function, just use a pointer to a member function
> instead, and pass from the most derived to the base:
[snip]

> struct base
> {
> EType m_eType;
>
> typedef void ( base:: * _TyMFnPtrInit )();
>
> base( _TyMFnPtrInit _pmfnInit = &base::init )
> {
> (this->*_pmfnInit)();
> }
>
> void init()
> {
> m_eType = eBase;
> }
Note that this only works because base::init() is *not* virtual. If
init() is virtual, base::init() will be the only function ever called.

> };
>
> struct derived : public base
> {
> derived()
> // Pass pointer to derived::init() initialization method.
> // In some cases a static_cast< _TyMFnPtrInit >() is necessary.
> : base( &derived::init )

"In some cases???" Either it's required or it isn't. In this case, it
is required, and any compiler that lets you compile without it is
broken.

The very fact that you need to use static_cast should signal to you that
you are doing something dangerous and non-portable. If init() attempts
to access any member of derived, you will end up in the realm of
undefined behaviour, since the derived portion of the object has not yet
been initialized.

[snip]
--
Jim
I ignore all email from recruitment agencies. Except that
I reserve the right to send rude, nasty replies to recruiters.
Please do not send me email with questions - post
here.


Sent via Deja.com http://www.deja.com/
Before you buy.

Lornis Van Loon

unread,
Apr 5, 2000, 3:00:00 AM4/5/00
to
On 4 Apr 2000 16:53:46 -0400, Jim Hyslop <jim.h...@leitch.com>
wrote:

>In article <38e98697.44727167@news>,
>lorn...@hotmail.com (Lornis Van Loon) wrote:
>> how about the, following - since a virtual function is really only a
>> pointer to a member function, just use a pointer to a member function
>> instead, and pass from the most derived to the base:
>[snip]
>> struct base
>> {
>> EType m_eType;
>>
>> typedef void ( base:: * _TyMFnPtrInit )();
>>
>> base( _TyMFnPtrInit _pmfnInit = &base::init )
>> {
>> (this->*_pmfnInit)();
>> }
>>
>> void init()
>> {
>> m_eType = eBase;
>> }
>Note that this only works because base::init() is *not* virtual. If
>init() is virtual, base::init() will be the only function ever called.

yes, of course, which is why i didn't declare it virtual


>
>> };
>>
>> struct derived : public base
>> {
>> derived()
>> // Pass pointer to derived::init() initialization method.
>> // In some cases a static_cast< _TyMFnPtrInit >() is necessary.
>> : base( &derived::init )
>"In some cases???" Either it's required or it isn't. In this case, it
>is required, and any compiler that lets you compile without it is
>broken.

The intel compiler must be broken.


>
>The very fact that you need to use static_cast should signal to you
that
>you are doing something dangerous and non-portable. If init() attempts
>to access any member of derived, you will end up in the realm of
>undefined behaviour, since the derived portion of the object has not
yet
>been initialized.

i wouldn't say this is non-portable. It may produce undefined behavior
if, for instance, an unitialized member is used. The very fact that
static_cast<> works and reinterpret_cast<> is not required indicates
that it is certainly a reasonable cast - i.e. that the compiler ( and
perhaps the C++ abstract machine ) accepts it as castable.
Would you say it was non-portable to cast a member function pointer of
a derived to a base when not in the ctor/dtor?

chris

unread,
Apr 5, 2000, 3:00:00 AM4/5/00
to
Why can't you use the classic 'two steps' construction phase ?
if not, you could try to adapt the virtual constructor (also known as
Factory Method ) pattern ( Design Pattern, Addison-Wesley, 97 ) to your
pb.

hope it helps.
chris.

Lornis Van Loon <lorn...@hotmail.com> wrote in message
news:38e98697.44727167@news...


> how about the, following - since a virtual function is really only a
> pointer to a member function, just use a pointer to a member function
> instead, and pass from the most derived to the base:
>

> #include <assert.h>
>
> enum EType
> {
> eBase,
> eDerived
> };
>

> struct base
> {
> EType m_eType;
>
> typedef void ( base:: * _TyMFnPtrInit )();
>
> base( _TyMFnPtrInit _pmfnInit = &base::init )
> {
> (this->*_pmfnInit)();
> }
>
> void init()
> {
> m_eType = eBase;
> }

> };
>
> struct derived : public base
>

> derived()
> // Pass pointer to derived::init() initialization method.
> // In some cases a static_cast< _TyMFnPtrInit >() is necessary.
> : base( &derived::init )

Lornis Van Loon

unread,
Apr 6, 2000, 3:00:00 AM4/6/00
to
This accomplishes the same thing as the post-construct idea, and
allows the method to be virtual ( though the usefulness of this
virtual-ity is questionable ):

#include <assert.h>

enum EType
{
eBase,
eDerived
};

struct base
{
EType m_eType;

base( bool _fIsMostDerived = true )
{
if ( _fIsMostDerived )
{
init();
}
}

virtual void init()
{
m_eType = eBase;
}
};

struct derived : public base
{
derived( bool _fIsMostDerived = true )
: base( false )
{
if ( _fIsMostDerived )
{
init();
}
}

void init()
{
m_eType = eDerived;
}
};

int
main()
{
base b;
derived d;
assert( eBase == b.m_eType );
assert( eDerived == d.m_eType );
}

and also, with an initializing template constructor, that takes an
initializer object, though this seems a bit contrived.

One interesting thing - the intel compiler fails to accept the
templatized contructor with a default argument as the default
constructor, is this ANSI ?

struct __empty_ctor { };

struct base;

struct base_init
{
void operator ()( base & _r );
};

struct base
{
EType m_eType;

base( )
{
base_init()( *this );
}
template < class t_TyInit >
base( t_TyInit _init )
{
_init( *this );
}
base( __empty_ctor )
{
}
};

inline void base_init::operator ()( base & _r )
{
_r.m_eType = eBase;
}

struct derived;

struct derived_init
{
void operator ()( derived & _r );
};

struct derived : public base
{
derived( )
: base( __empty_ctor() )
{
derived_init()( *this );
}
template < class t_TyInit >
derived( t_TyInit _init )
: base( __empty_ctor )
{
_init( *this );
}
derived( __empty_ctor )
{
}
};

inline void derived_init::operator ()( derived & _r )
{
_r.m_eType = eDerived;
}

LVL

Frans Meijer

unread,
Apr 6, 2000, 3:00:00 AM4/6/00
to
In article <38e98697.44727167@news>,
lorn...@hotmail.com (Lornis Van Loon) wrote:
> how about the, following - since a virtual function is really only a
> pointer to a member function, just use a pointer to a member function
> instead, and pass from the most derived to the base:
>
> #include <assert.h>
>
> enum EType
> {
> eBase,
> eDerived
> };
>
> struct base
> {
> EType m_eType;
>
> typedef void ( base:: * _TyMFnPtrInit )();
>
> base( _TyMFnPtrInit _pmfnInit = &base::init )
> {
> (this->*_pmfnInit)();
> }
>
> void init()
> {
> m_eType = eBase;
> }
> };
>
> struct derived : public base
> {
> derived()
> // Pass pointer to derived::init() initialization method.
> // In some cases a static_cast< _TyMFnPtrInit >() is necessary.
> : base( &derived::init )
> { }
>
> void init()
> {
> m_eType = eDerived;
> }
> };
>
> int
> main()
> {
> base b;
> derived d;
> assert( eBase == b.m_eType );
> assert( eDerived == d.m_eType );
> }
>
> rock on, Lornis Van Loon
>

Why use a member function at all? Bengt indicated that the derived
objects state wasn't used, so a non member (non friend) would be
suitable. Since no state is used, no object is needed either... (what is
this function supposed to do anyway?)

struct base
{
base(char*(*ptype)()) { ptype(); }

};

struct derived : public base
{
derived();
};

char* ptype_derived() {}

derived::derived() : base(ptype_derived)
{
// ...
}

or

template<class T> char* pType(){}
derived::derived : base(pType<derived>) {}

Off course, if some_init does needs a derived object, it would need a
fully constructed one. But so would the virtual....

And finally, why not pass this char* directly to the base's c'tor?

> >Thanks in advance. /Bengt Cyren
> >
>

Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Jim Hyslop

unread,
Apr 9, 2000, 3:00:00 AM4/9/00
to
In article <38ea6b9a.7476541@news>,

lorn...@hotmail.com (Lornis Van Loon) wrote:
[snip]

> Would you say it was non-portable to cast a member function pointer of
> a derived to a base when not in the ctor/dtor?
Yes, because in the case of a non-virtual function, a derived-class
member function may not be a base-class member function. In the case of
a virtual function, the base class may be pure without an
implementation. In either case, you are asking for trouble.

--
Jim
I ignore all email from recruitment agencies. Except that
I reserve the right to send rude, nasty replies to recruiters.
Please do not send me email with questions - post
here.

Lornis Van Loon

unread,
Apr 11, 2000, 3:00:00 AM4/11/00
to
On 9 Apr 2000 09:53:57 -0400, Jim Hyslop <jim.h...@leitch.com>
wrote:

>In article <38ea6b9a.7476541@news>,


> lorn...@hotmail.com (Lornis Van Loon) wrote:
>[snip]
>> Would you say it was non-portable to cast a member function pointer
of
>> a derived to a base when not in the ctor/dtor?
>Yes, because in the case of a non-virtual function, a derived-class
>member function may not be a base-class member function. In the case
of
>a virtual function, the base class may be pure without an
>implementation. In either case, you are asking for trouble.

Can you point me to the specific ANSI sections describing that
undefined behavior is the result of casting a non-virtual member
function from a derived class to a non-virtual base class ?

In *all* the cases where I use member function casting ( which is
incredibly useful, i might add ), there is never a base class member
with the same name. The fact that the compiler specifically forbids
casting a function pointer to a virtual base class leads me to believe
that the compiler was designed to allow member function pointer
casting. It seems to be well defined to me, can you explain
situation/implementation in which it would not be ?

Stefan Seefeld

unread,
Apr 11, 2000, 3:00:00 AM4/11/00
to
as others already explained, calling a (virtual) method
on an object which isn't yet fully constructed is a bad idea.
I think the solution is not to try to find holes in the
semantics of by other means to obscure the situation.

You should only call virtual methods on fully constructed
objects. The simple solution seems to be to split your object
into two parts Inner and Outer and pass a pointer to an Inner
to the constructor of the Outer:

class Outer
{
Outer(Inner *i) : inner(i)
{
//. now make use of inner, it is completely valid to call
//. inner's virtual methods here, since the object's construction
//. had been completed before this constructor was entered
}
private:
Inner *inner;
};

and then construct your class hierarchy on top of these.
class MyInner : public Inner {/*...*/};
class MyOuter : public Outer {/*...*/};

MyOuter outer(new MyInner);

In fact, may be it's not even needed to build a hierarchy around
Outer. It may just be a convenience layer on top of 'Inner'.
The iostream library is a nice example with iostream being the 'Outer'
and streambuf being the 'Inner'.

Regards, Stefan
_______________________________________________________

Stefan Seefeld
Departement de Physique
Universite de Montreal
email: seef...@magellan.umontreal.ca

_______________________________________________________

...ich hab' noch einen Koffer in Berlin...

Jim Hyslop

unread,
Apr 13, 2000, 3:00:00 AM4/13/00
to
In article <38f27fda.24243033@news>,

lorn...@hotmail.com (Lornis Van Loon) wrote:
> On 9 Apr 2000 09:53:57 -0400, Jim Hyslop <jim.h...@leitch.com>
> wrote:
>
> >In article <38ea6b9a.7476541@news>,
> > lorn...@hotmail.com (Lornis Van Loon) wrote:
> >[snip]
> >> Would you say it was non-portable to cast a member function pointer
> of
> >> a derived to a base when not in the ctor/dtor?
> >Yes, because in the case of a non-virtual function, a derived-class
> >member function may not be a base-class member function. In the case
> of
> >a virtual function, the base class may be pure without an
> >implementation. In either case, you are asking for trouble.
> Can you point me to the specific ANSI sections describing that
> undefined behavior is the result of casting a non-virtual member
> function from a derived class to a non-virtual base class ?
4.11 describes the standard pointer-to-base-member to
pointer-to-derived-member conversion. It ends with footnote 52, which
describes why 4.11 appears to be inverted with respect to conversion of
pointers to derived objects: type safety.

> In *all* the cases where I use member function casting ( which is
> incredibly useful, i might add ), there is never a base class member
> with the same name.

Names don't have anything to do with it. Converting a derived class
member function to a base class member function is dangerous because the
derived class function expects to operate on a *derived* class. I
didn't state that very well in my previous post.

> The fact that the compiler specifically forbids
> casting a function pointer to a virtual base class leads me to believe
> that the compiler was designed to allow member function pointer
> casting. It seems to be well defined to me, can you explain
> situation/implementation in which it would not be ?

Consider the following example:

#include <iostream>

class base
{
int i;
public:
base() : i(1) {}
int f() { return i; }
};

class derived : public base
{
int j;
public:
derived() : j(2) {}
int g() {
std::cout << "In derived::g()\n";
return j;
}
};

int main()
{
typedef int (base::*PMF_BASE)();
PMF_BASE pmf=static_cast<PMF_BASE>(&derived::g);
base b;
int value = (b.*pmf)();
std::cout << value << std::endl;
return 0;
}

If you compile and run this code, chances are you will get garbage
output for the value, because you are running derived::g() on a base
object - there *is* no derived data to work with.

That is why you cannot implicitly convert from a pointer-to-derived to a
pointer-to-base, and you must explicitly cast the value. Remember,
casting is a sign that you are doing something potentially unsafe. If
derived::g() did not attempt to access any members of derived, chances
are your program will be fine.

--
Jim
I ignore all email from recruitment agencies. Except that
I reserve the right to send rude, nasty replies to recruiters.
Please do not send me email with questions - post
here.


Sent via Deja.com http://www.deja.com/
Before you buy.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Lornis Van Loon

unread,
Apr 14, 2000, 3:00:00 AM4/14/00
to
Needless to say, i agree that calling a method for a derived object on a
base
object that is not that derived object is a bad idea. That was not what
I was
suggesting. It is certainly safe, if you know that the object is of
derived type
whose address of member function u have.

thanks, LVL

0 new messages