Topic: Class instanciation and virtual stuff..


Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/04/22
Raw View
ldh@interlog.com (Laurent-David Hasson) writes:

>  I have a question about how objects are constructed, and when "virtual"
>services become available.

>  I remember reading in the ARM that one should not call "virtual" functions
>from a constructor. The reason being that an object should not be considered
>fully initialized until the last constructor had ended.

Once you enter a constructor body, virtual services are available,
and you may call virtual functions if you want. However, the type
of the object at that time is the constructor's own type, and
not (necessarily) the type of the complete object. Thus, the
version of the virtual function which gets called does not depend
on the type of the complete object ultimately being constructed.

Consider an example:

 class B {
 public:
  B() { f(); }
  virtual void f();
 };
 class D : public B {
 public:
  D() { f(); }
  virtual void f(); // override B::f
 };

 B b; // #1
 D d; // #2

At #1, we construct a B object, and B::B() calls B::f(), all of
which I hope is obvious.

At #2, we construct a D object. The first step of construction
is to invoke B::B(). Upon entry to B::B(), the object has type B,
not type D, and so B::f() gets called, just as in #1. The reason
for the rule is that D's version of f might depend on data in D
which doesn't yet exist. Some other languages have different rules,
but these are the C++ rules.

After constructing the B part of D, we enter the body of D. At
this time the object has type D, and in D::D() when we call
f() we get D::f().

If it turns out we were really constructing some object derived
from D, the actions in B::B() and D::D() would not change.

The same holds for destructors. When D is destroyed, the last step
is to invoke the B destructor. During the run of that destructor,
the object no long has type D, but again has type B, so no
functions from D will be called.

>  However, by mistake, in one of my program, i was calling a library function
>which is calling virtual functions. I never noticed the error because
>apparently, the compiler i was using at the time allowed this behavior to
>work properly. However, i recently got a new version of the compiler, and
>everything broke. It took me several days to realize what was happening.

Perhaps virtual base classes were involved. Most compilers have
historically had a bug wherein indirect calls to virtual functions
from constructors via non-member functions in the presence of virtual
base classes and multiple inheritance sometimes (but not always) turned
into garbage at run time.  The bug is quite subtle and hardly ever gets
triggered. The details are far too complicated to explain here.

I believe all compiler implementors are aware of this problem and
how to solve it, although the solutions are quite complicated
and may not yet be implemented everywhere. If you don't pass "this"
from a constructor or destructor to an outside function, I don't
think you will ever encounter the problem. If virtual base classes
and multiple inheritance are not involved, you won't encounter
the problem.

--
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
]





Author: claus@faerber.muc.de (Claus A. Faerber)
Date: 1996/04/22
Raw View
CCed to ldh@interlog.com & comp.std.c++

Laurent-David Hasson <ldh@interlog.com> (20 Apr 96):

[...]
>   I am now left with doubts with respect to the availibility of "virtual"
> services, and i am now wondering why it is the way it is, that is, why a
> class type should not be known, and all the plumbing in place, before the
> first constructor is called. Naively, i would assume that instanciating an
> object would go as follows:
>       - Allocate the memory for the whole class
      ---
>       - Create the suporting structural stuff (like typeID, VT etc...)
>       - Then call the constructor, which follows with the Yo-Yo construction
>        to initialize all the data members and whatever.

No, most compilers create such ctors:
        - Call the base constructor(s)
        - Then set the own VT, typeID, ...
        - Execute constructor body

>   This makes sense to me because the type of the class would actualy be
> known in the constructor of the base class.

Setting the VT and typeID is done by the ctor; as the base
ctor goes first, it sets VT & typeID corresponding to its
class type. The child's ctor (s.above) then in turn has to
set its own VT & typeID.

Such an implementaion (I do not know whether the standard
requires this to be done exactly this way - I doubt it.)
would cause exactly the behaviour you encountered:

> Instead, calling "typeid" on
> "this" in the base constructor gets the base class' typename.

> I understand
> why one should not lightly call a virtual function because the local vars
> would not have been initialized yet.

The local vars are not the problem, it is only the VT
pointer; at the time your ctor body is called, it can -
depending on the compiler - point anywhere including the VT
of a base class, your own VT or nowhere.
(Normally it would point to the VT of the class the ctor
currently executed belongs to.)

> But what if i called a virtual function
> that did initialize vars it needed (never access a var of the class before
> it has been written to first)? Or a virtual function that does not use the
> local members?  Why should that fail?
>
>  This leads me to think that all the "virtual" things are actually taken
> care  of AFTER all construction has been made.

Depending on the compiler, this _may_ be true.

> And i do not understand why
> this is. Are there important penalties i do not understand, or pitfalls i do
> not see?

Claus

-------------------------------------------------------------------------
Claus Andre Faerber - claus@faerber.muc.de - http://www.muc.de/~cfaerber/
-------------------------------------------------------------------------
---
[ 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: ettienne@agate.net (Steve Nutt)
Date: 1996/04/23
Raw View
ldh@interlog.com (Laurent-David Hasson) wrote:

>  I have a question about how objects are constructed, and when "virtual"
>services become available.

>  I remember reading in the ARM that one should not call "virtual" functions
>from a constructor. The reason being that an object should not be considered
>fully initialized until the last constructor had ended.

>  However, by mistake, in one of my program, i was calling a library function
>which is calling virtual functions. I never noticed the error because
>apparently, the compiler i was using at the time allowed this behavior to
>work properly. However, i recently got a new version of the compiler, and
>everything broke. It took me several days to realize what was happening.

>  I am now left with doubts with respect to the availibility of "virtual"
>services, and i am now wondering why it is the way it is, that is, why a
>class type should not be known, and all the plumbing in place, before the
>first constructor is called. Naively, i would assume that instanciating an
>object would go as follows:
>      - Allocate the memory for the whole class
>      - Create the suporting structural stuff (like typeID, VT etc...)
>      - Then call the constructor, which follows with the Yo-Yo construction
>       to initialize all the data members and whatever.
>
>  This makes sense to me because the type of the class would actualy be known
>in the constructor of the base class. Instead, calling "typeid" on "this" in
>the base constructor gets the base class' typename. I understand why one
>should not lightly call a virtual function because the local vars would not
>have been initialized yet. But what if i called a virtual function that did
>initialize vars it needed (never access a var of the class before it has been
>written to first)? Or a virtual function that does not use the local
>members?  Why should that fail?

> This leads me to think that all the "virtual" things are actually taken care
> of AFTER all construction has been made. And i do not understand why this
>is. Are there important penalties i do not understand, or pitfalls i do not
>see?



>Thanks,
>Laurent.


>PS: CC by email is much appreciated.

>============================================================================
>Microsoft Network, or any service owned in full or in part by Microsoft, is
>prohibited from redistributing this work in any form, in whole or in part.
>Copyright, Laurent Hasson, 1996. License to distribute this post is
>available to Microsoft for US$10,000 per instance. Posting without
>permission constitutes an agreement to these terms. Please send notices of
>violation to ldh@interlog.com and postmaster@microsoft.com
>============================================================================
>---
>[ 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                             ]


Well see if this example helps.

class A
{
public:
 A (void);
 virtual ~A (void) {};
};

int
class B
{
public:
 B (void) : x (1)
 {
 assert (dynamic_cast<B*> (this) == this);
 }

 const int x;
};

A::A (void)
{
 assert ((void*) this == dynamic_cast<void*> (this));
 assert (dynamic_cast<B*> (this) == 0);
}

If I created an object of type B,  then in A::A it is only an A.  It
is not a B yet.  If it was,  then the dynamic cast would suceed,  and
I could access B::x.  which has not yet been constructed.

Once the constructor in B has been entered the object becomes a B.  I
can call virtaul functions in constructors and destructors  but the
functions called will not be member functions in any derived classes
as they have not yet been constructed,  or have since been destructed.

Steve
---
[ 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: ldh@interlog.com (Laurent-David Hasson)
Date: 1996/04/20
Raw View
  I have a question about how objects are constructed, and when "virtual"
services become available.

  I remember reading in the ARM that one should not call "virtual" functions
from a constructor. The reason being that an object should not be considered
fully initialized until the last constructor had ended.

  However, by mistake, in one of my program, i was calling a library function
which is calling virtual functions. I never noticed the error because
apparently, the compiler i was using at the time allowed this behavior to
work properly. However, i recently got a new version of the compiler, and
everything broke. It took me several days to realize what was happening.

  I am now left with doubts with respect to the availibility of "virtual"
services, and i am now wondering why it is the way it is, that is, why a
class type should not be known, and all the plumbing in place, before the
first constructor is called. Naively, i would assume that instanciating an
object would go as follows:
      - Allocate the memory for the whole class
      - Create the suporting structural stuff (like typeID, VT etc...)
      - Then call the constructor, which follows with the Yo-Yo construction
       to initialize all the data members and whatever.

  This makes sense to me because the type of the class would actualy be known
in the constructor of the base class. Instead, calling "typeid" on "this" in
the base constructor gets the base class' typename. I understand why one
should not lightly call a virtual function because the local vars would not
have been initialized yet. But what if i called a virtual function that did
initialize vars it needed (never access a var of the class before it has been
written to first)? Or a virtual function that does not use the local
members?  Why should that fail?

 This leads me to think that all the "virtual" things are actually taken care
 of AFTER all construction has been made. And i do not understand why this
is. Are there important penalties i do not understand, or pitfalls i do not
see?



Thanks,
Laurent.


PS: CC by email is much appreciated.

============================================================================
Microsoft Network, or any service owned in full or in part by Microsoft, is
prohibited from redistributing this work in any form, in whole or in part.
Copyright, Laurent Hasson, 1996. License to distribute this post is
available to Microsoft for US$10,000 per instance. Posting without
permission constitutes an agreement to these terms. Please send notices of
violation to ldh@interlog.com and postmaster@microsoft.com
============================================================================
---
[ 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                             ]