Board index » cppbuilder » Re: Virtual method in constructor : possible ?

Re: Virtual method in constructor : possible ?


2006-03-20 05:37:25 AM
cppbuilder6
At 21:13:34, 19.03.2006, Alisdair Meredith[TeamB] wrote:
Quote
Rudy Velthuis [TeamB] wrote:

>Which doesn't solve the problem, then, IMO.

No, but it minimizes it. This way you have a guarantee your own
members are initialized. I you construct bases first you have exactly
zero guarantees - as you don't know which bases may or may not have
been constructed.

>And it is a bit weird, since Delphi for .NET requires that a call to a
>base class constructor is the first statement in the body of a
>constructor. Seems they chose the exact opposite of C++/CLI, for one
>reason or other.

Copying Java? <ducks/>

The key problem here is that dynamic dispatch during
construction/destruction sucks, does very strange things at unexpected
times, and generally causes chaos for those caught unaware.
And yet it doesn't seems to cause many problems I am aware of. I hardly
see people having problems with this.
Quote
Also, I'm not entirely sure if C# and Java offer a similar guarantee to
Delphi that they will initialize all member-object refernces to null.
AFAIK, C# does (or, actually, .NET does). I have no idea whether Java
does.
Quote
If so, they are effectively calling a hidden default ctor for the class
that you cannot replace - and one that initializes members before bases.
In Delphi (for Win32, at least), you can replace it. It would be foolish,
but you could overwrite NewInstance to set all members to bytes of 0x55,
or some such, and also to put your objects on the stack, or some special
heap. NewInstance calls GetMem and then InitInstance, which is static,
and sets everything to 0 and initiliases VMT and interface table
pointers, but NewInstance is virtual, and could just as well do something
differently.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"I have often regretted my speech, never my silence."
- Xenocrates (396-314 B.C.)
 
 

Re:Re: Virtual method in constructor : possible ?

"Lionel Reynaud" < XXXX@XXXXX.COM >wrote in message
Quote
Many thanks for your c++ courses. Effectively my understanding of
how C++ works is not optimal !! That's because i work generaly
with Delphi.
The creation order of objects in Delphi is the opposite than it is in C++.
In Delphi, descendant classes are created first, then base classes. In C++,
base classes are created first, then descendant classes. That is why Delphi
constructors can access descendant members whereas C++ constructors cannot.
Quote
Here is the same code with Delphi who work as i expected !!!!
That code cannot work in C++.
Gambit
 

Re:Re: Virtual method in constructor : possible ?

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
Quote
I personally have not seen code where it is constructed last.
I have. There are places in the VCL itself where the base class constructor
is not called first. And I have seen third-party libraries that have places
where the base class constructor is not called first.
Gambit
 

{smallsort}

Re:Re: Virtual method in constructor : possible ?

At 01:36:17, 20.03.2006, Remy Lebeau (TeamB) wrote:
Quote

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
news:xn0ejx8qb6pp2nu019-velthuis@www.teamb.com...

>I personally have not seen code where it is constructed last.

I have. There are places in the VCL itself where the base class
constructor is not called first. And I have seen third-party libraries
that have places where the base class constructor is not called first.
"Not called first" != "called last". I have seen code where some items
were initialised before the base class constructor (inherited
constructor, as Delphians say) too, but I have never seen code where the
base class was constructed last.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"Java, the best argument for Smalltalk since C++." -- unknown
 

Re:Re: Virtual method in constructor : possible ?

At 01:32:27, 20.03.2006, Remy Lebeau (TeamB) wrote:
Quote

"Lionel Reynaud" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>Many thanks for your c++ courses. Effectively my understanding of
>how C++ works is not optimal !! That's because i work generaly
>with Delphi.

The creation order of objects in Delphi is the opposite than it is in
C++.
Just like what Liz said, that is wrong. It is not opposite, it just not
always the same. But generally, it is the same as in C++.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"Giving birth is like taking your lower lip and forcing it over
your head." - Carol Burnett.
 

Re:Re: Virtual method in constructor : possible ?

"Duane Hebert" < XXXX@XXXXX.COM >writes:
Quote
class base {
public:
base();
virtual void spoo();
};

class derived :public base{
public:
derived() { spoo();}
void spoo();
};

Since derived isn't complete when calling spoo(), it's likely that
base::spoo() gets called.
This is not about "likely" or "unlikely". It's about what the language
requires.
The language requires that derived::spoo be invoked.
Quote
>And what about virtual functions added in the derived class?

I think if the function is declared in the class that is being
constructed it should be no different than any other function
in that class. No? I mean, it's the only place that the function
can be found at that point, no?
Correct. But this is the case that often surprises Java and Delphi
programmers, because these languages didn't get it right.
Quote
www.artima.com/cppsource/nevercall.html
Hmm, "Never call virtual functions during construction or destruction"
I don't buy it. I'd say just have the right expectations.
 

Re:Re: Virtual method in constructor : possible ?

"Thomas Maeder [TeamB]" < XXXX@XXXXX.COM >wrote in message
Quote
>Since derived isn't complete when calling spoo(), it's likely that
>base::spoo() gets called.

This is not about "likely" or "unlikely". It's about what the language
requires.

The language requires that derived::spoo be invoked.
According to Meyers (from the link that I posted):
<quote>
There's a good reason for this seemingly counterintuitive behavior. Because
base class constructors execute before derived class constructors, derived
class data members have not been initialized when base class constructors
run. If virtual functions called during base class construction went down to
derived classes, the derived class functions would almost certainly refer to
local data members, but those data members would not yet have been
initialized. That would be a non-stop ticket to undefined behavior and
late-night debugging sessions. Calling down to parts of an object that have
not yet been initialized is inherently dangerous, so C++ gives you no way to
do it.
</quote>
Either I'm misinterpreting this or he seems to be saying that
trying to invoke the derived spoo is UB.
Quote
>www.artima.com/cppsource/nevercall.html

Hmm, "Never call virtual functions during construction or destruction"

I don't buy it. I'd say just have the right expectations.
From the above link, I would expect it to have UB. Like
I say, maybe I'm missing something.
 

Re:Re: Virtual method in constructor : possible ?

"Duane Hebert" < XXXX@XXXXX.COM >writes:
Quote
"Thomas Maeder [TeamB]" < XXXX@XXXXX.COM >wrote in message
news: XXXX@XXXXX.COM ...

>>Since derived isn't complete when calling spoo(), it's likely that
>>base::spoo() gets called.
>
>This is not about "likely" or "unlikely". It's about what the language
>requires.
>
>The language requires that derived::spoo be invoked.

According to Meyers (from the link that I posted):
<quote>
There's a good reason for this seemingly counterintuitive behavior. Because
base class constructors execute before derived class constructors, derived
class data members have not been initialized when base class constructors
run. If virtual functions called during base class construction went down to
derived classes, the derived class functions would almost certainly refer to
local data members, but those data members would not yet have been
initialized. That would be a non-stop ticket to undefined behavior and
late-night debugging sessions. Calling down to parts of an object that have
not yet been initialized is inherently dangerous, so C++ gives you no way to
do it.
</quote>

Either I'm misinterpreting this or he seems to be saying that
trying to invoke the derived spoo is UB.
In your earlier post you were talking about calling the virtual
function from inside the derived class constructor. But the text
above is talking about why when you call a virtual function while
inside the base constructor it doesn't go "down into the derived part
of the class."
There are two types to an object: the static and the dynamic types.
If you have a pointer to base, the static type is base. The dynamic
type is "whatever it actually points to".
Calling a virtual function calls the function on the current dynamic
type of the object.
So, when an object is fully constructed, the dynamic type is always
the most derived type. However, during construction, the dynamic type
actually changes! When the base part is constructing, the dynamic
type is actually set to be the static part. When the base constructor
completes, before the derived constructor begins executing, the
dynamic type changes to the next derived type. When that constructor
finishes, the next-derived type becomes the dynamic type, and so on,
until it has reached the most-derived type.
The way this is implemented is by changing the vtbl pointer during
construction. If you could read the vtbl pointer from your
constructors at every level, you'd notice that upon entry of each
constructor the address changes. (RTTI information is stored in the
vtbl, and so changing the vtbl changes the dynamic type, and it also
changes how virtual functions are resolved.)
While vtbls are implementation details, they're how virtual functions
are implmeneted on every c++ compiler I know of, and cannot imagine
anyone ever writing it differently.
But the point is, if your constructor is running for a sub-object of
type T, then the dynamic type of the object is T, and so all of the
virtual function calls from the constructor (or destructor) will end
up invoking functions on the current object part being constructed.
(Except for pure-virtuals, which result in undefined behavior when
called in the context of constructors or destructors.)
--
Chris (TeamB);
 

Re:Re: Virtual method in constructor : possible ?

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
Quote
"Not called first" != "called last".
I have seen Pascal code that calls the base class constructor first, in the
middle, and last.
Gambit
 

Re:Re: Virtual method in constructor : possible ?

At 19:39:25, 20.03.2006, Remy Lebeau (TeamB) wrote:
Quote

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
news:xn0ejyddk7rn046000-velthuis@www.teamb.com...

>"Not called first" != "called last".

I have seen Pascal code that calls the base class constructor first, in
the middle, and last.
Then you must have seen more than I have.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"Don't knock {*word*187}, it's sex with someone I love ."
-- Woody Allen, From 'Annie Hall' 1977.
 

Re:Re: Virtual method in constructor : possible ?

At 19:39:25, 20.03.2006, Remy Lebeau (TeamB) wrote:
Quote

"Rudy Velthuis [TeamB]" < XXXX@XXXXX.COM >wrote in message
news:xn0ejyddk7rn046000-velthuis@www.teamb.com...

>"Not called first" != "called last".

I have seen Pascal code that calls the base class constructor first, in
the middle, and last.
Perhaps, but in the middle and last are very rare exceptions. Usually,
the order of construction is the same as in C++, and for the same reason
as in C++.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"Not only is there no God, but you try getting a plumber at weekends."
-- Woody Allen.
 

Re:Re: Virtual method in constructor : possible ?

Quote
So, when an object is fully constructed, the dynamic type is always
the most derived type. However, during construction, the dynamic type
actually changes! When the base part is constructing, the dynamic
type is actually set to be the static part. When the base constructor
completes, before the derived constructor begins executing, the
dynamic type changes to the next derived type. When that constructor
finishes, the next-derived type becomes the dynamic type, and so on,
until it has reached the most-derived type.
This is the bit that I was confused about. Thanks. I thought that since
I was in the ctor for the derived class, the only object complete at
that point would be the base and the virtual function from the base
would be invoked as the derived's version of the virtual function
was still unknown.
Actually, after re-reading the link from Meyers, I'm still thinking that
that's what he's saying <g>
 

Re:Re: Virtual method in constructor : possible ?

Quote
Perhaps, but in the middle and last are very rare exceptions. Usually,
the order of construction is the same as in C++, and for the same reason
as in C++.
Well "usually" is one of those words that can bite you
in computer science.
 

Re:Re: Virtual method in constructor : possible ?

At 22:02:12, 20.03.2006, Duane Hebert wrote:
Quote
>Perhaps, but in the middle and last are very rare exceptions. Usually,
>the order of construction is the same as in C++, and for the same
>reason as in C++.

Well "usually" is one of those words that can bite you
in computer science.
What I mean is that although one must be careful, the problem is not as
big as it may look.
--
Rudy Velthuis [TeamB] rvelthuis.de/
"Why don't they make the whole plane out of that black box stuff."
-- Steven Wright.
 

Re:Re: Virtual method in constructor : possible ?

Rudy Velthuis [TeamB] wrote:
Quote
Just like what Liz said, that is wrong. It is not opposite, it just
not always the same. But generally, it is the same as in C++.
"It is the same apart from when it is not" is usually an accurate
statement, but not overly useful ;?
Delphi construction is quite different.
First, the memory to be occupied by your object is zeroed. This
'pre-constructor' has no equivalent in C++, but provides essential
guarantees later in the process. It also means all your data members
*are initialized* before you get into user-written code.
Then the most-derived ctor is called, and can do whatever it likes,
including not calling any base class ctor at all. After all, the
object was zero-initialized, so all data members are defined.
However, if it chooses to chain a base class ctor, it does so at a
moment of its choosing. Conventionally, but not necessarily first.
Base class ctors do not get the zero-initialization pre-constructor,
that is (for obvious reasons) fired at the most-derived level only. So
execution proceed directly to ctor body. During this ctor, virtual
functions will dispatch to the most derived type. That is the reason
everything is zero-initialized, to give you something to safely check.
Likewise, if you rely on certain data being available during virtual
calls - you can initialize those members before calling your base ctor.
This fundamentally changes the contract between base and derived
classes. Guarantees you rely on in C++ do not hold, likewise
additional guarantees are expected for the Delphi behaviour. This is
no accident as places in the VCL rely on this behaviour, whether a
given user chooses to code exclusively in the C++ style or not
(although if they do so, you wonder why they are using Delphi in the
first place, or at least crippling their ability to use it effectively)
Finally, after all ctors have finished executing, the AfterConstruction
virtual function is called. Again, their is no C++ equivalent.
So by user dilligence you can choose to treat Delphi construction as
C++ construction - by following conventions to never call virtual
functions (or functions that in turn call virtual functions), always
chaining you base class ctor first, never taking advantage of the
implicit zero-initialization of your memory, and never overriding the
AfterConstruction function. Oh, and never deriving from a 3rd party
base class that does not follow these rules as well.
Otherwise, accept the languages are different for a reason and use them
for their strengths.
One thing I was never entirely sure on is what happens in Delphi
destructors when a constructor throws an exception. Which destructors
are called, and in what sequence? Are dtors called for base classes
that were never constructed? Does destruction start with the
most-derived dtor first? The Delphi language guide I learned from
advised writing destructors to cope with partially initialized classes,
but ducked on the details of how you might do that.
Generally, I find the difference between the two models that are
'mostly the same' strikes when trying to write code that 'really works'
rather than 'mostly works'. At that point, you have to consider you
constructor/destructor model, and write code appropriately.
End of lecture!
--
AlisdairM(TeamB)