I don't have to tell you...

B

Balog Pal

Alf P. Steinbach said:
The incompetence of experienced people is a problem with no known
solution.

You say that at least some people, and I assume that you mean experienced
ones, expect the language to behave in a nonsensical way, like some other
language they're familiar with.

I don't say that. :) The observation is that people just drop in the
virtual call. And start to thinker on the language behavior after getting
burnt.

And not necessarily "expect" the thing work this or that way. The pragmatic
way is to do the work anyway instead of dreaming about a different world. :)

Guidelines just help to minimize the wasted time. Not going there in the
first place or having the relevant info faster at least.
And it seems that you're implicitly arguing that because of their
nonsensical expectation the safe behavior is a problem.

That since they practically can't be made competent, the language should
be dumbed down to their level, removing safety features they fail to
understand.

That's shadow-boxing. ;-)

I don't have a problem of how C++ deals with virtuals, and most likely would
chose the same behavior. And I also agree with the guideline that states
don't call virtuals in the situation.

Those who are competent and found the rare situation it makes sense or is is
just safely irrelevant will hardly be bothered by it.
No, the FAQ is mainly for novices.

A novice may expect C++ to work like some other language, e.g. like Java.

An experienced C++ programmer who expects that, however, is incompetent.

Sure. This has nothing to do with the point I made.

I am competent in programming and C++. I do know the behavior of virtuals.
Including this thing. I know it for ages, and at the pioneering period
worked even with the assy code of ctors/dtors. To know the exact point where
the VMT pointer is replaced (effectively changing the actial type of the
object at hand).

Yet, I recall at least one situation from my work I got hit by the related
problem -- having the wrong (meaning not the *intended*) function called
form a base destructor. Wasn't a big deal, and realized it in the first run
(probably it was a pure virtual with a noisy report).

But well demonstrates that shit just happens. ;-))
I have on some occasions dumbed down code so that it "should" be grokkable
even by idiots.

We know that is a futile effort, as nature immediately creates an advanced
idiot. And starts cloning it too.
You don't want to rearrange the correctly working C++ code.
So, C++: zero time.

Computers do what is ordered, not what was intended. The "correct" code
would be with the intended behavior. The fact that the C++ part safely does
the unintended thing will not make it fly.
This doesn't make sense. It seems you have some problem and some
particular based-on-misconceptions non-working solution to that problem in
mind. Don't blame that on the language: blame it on a fixation on a
non-working solution.

You can put it that way if you like, it won't make the problem go away.
The mind works in an interesting way. See the examples of the optical
illusions. (or how is that called when you see straight lines as curved,
equal length as different, same color as massively darker in a properly
created picture).

Similar fallacy is to expect things to "work". There was recently a
reference to Mark Rosewater's evil creations here or on a neighboring forum.
And that is exactly true.

And the more commonly a feature just does the job the less we think there
are limits.
What you're saying seems to be that you have suffered from the "C++ works
like Java" misconception, and fixed that by removing calls that you
believed would end up in a derived class.

Well that's not something to blame C++ for.

That is what I'm saying from the start. This problem is not related to C++,
but to the general thinking of virtuals. And a related genuine, theoretic
limitation.

Languages could create different behaviors, but none of them would be good
for all the cases.
 
H

Howard Beale

Alf said:
* Howard Beale:You made several factually incorrect claims, as I count it four of
them in the single paragraph of yours that I quoted in my first
response:

* Howard Beale:
In what order are constructors and destructors called? Answer - in
whatever order ANSI/ISO chooses to call them, and [1] you'll never
know just by looking at your code, and [2] you'll never be able to
change it
if it doesn't suit you. Does it matter? Yes - the choice that was
made means that [3] you should never call virtual functions from a
constructor or destructor, even though [4] the language will allow
you to do so with no warning, just incorrect behavior.

To which of these four incorrect claims (if any) are you referring?

None of them are supported by the FAQ.

Well, most of them don't need to be supported by anything, as they are
common knowledge.

[1] As in the example I gave (and any example that could be given), you
wouldn't know, by just looking at the code, that you're going to get the
wrong output. You would have to also know about the order in which
constructors / destructors are called, which is exactly what is at issue
here. You may say that the output isn't "wrong," but I personally
believe that if the area of a circle is pi*r^2 during its entire
existence, then it's area should also be pi*r^2 when it is being created
or destroyed. And most programmers, unaware of this little gotcha,
would say the same thing. You, being a C++ proponent and expert, know
differently, but most programmers prefer consistent behavior. Yes, I'll
grant that programmers also want safety, but it is entirely possible for
a compiler to safely allow virtual methods during construction and
destruction.

[2] Do you know of a way to change the order in which constructors /
destructors are called? I don't, outside of writing a new compiler.

[3] I guess "never" is a strong word, so I can meet you half way on this
statement, I suppose, and just say "don't expect virtual methods to work
correctly when called from a constructor or destructor, so call them
there only if you want incorrect behavior." Seems like a silly caveat.
If you feel that the behavior that I describe isn't "incorrect," then
you're too tolerant in your definition of correctness.

[4] My compiler doesn't give me any warning when I call a virtual method
from a constructor or destructor. I don't know what more to tell you.
 
H

Howard Beale

Joshua said:
Now, I know you're just trolling, but I'm easily baited.

.... and also unaware of the movie "Network." It was a goof on a famous
speech in that movie. It was mostly exaggeration and silliness. The first
reply to it included a link to the video. But what I said at the end,
after the speech, was sincere.
 
A

Alf P. Steinbach

* Howard Beale:
Alf said:
* Howard Beale:
Alf P. Steinbach wrote:

* Balog Pal:
You made several factually incorrect claims, as I count it four of
them in the single paragraph of yours that I quoted in my first
response:

* Howard Beale:
In what order are constructors and destructors called? Answer - in
whatever order ANSI/ISO chooses to call them, and [1] you'll never
know just by looking at your code, and [2] you'll never be able to
change it
if it doesn't suit you. Does it matter? Yes - the choice that was
made means that [3] you should never call virtual functions from a
constructor or destructor, even though [4] the language will allow
you to do so with no warning, just incorrect behavior.
To which of these four incorrect claims (if any) are you referring?

None of them are supported by the FAQ.

Well, most of them don't need to be supported by anything, as they are
common knowledge.

[1] As in the example I gave (and any example that could be given), you
wouldn't know, by just looking at the code, that you're going to get the
wrong output. You would have to also know about the order in which
constructors / destructors are called, which is exactly what is at issue
here. You may say that the output isn't "wrong," but I personally
believe that if the area of a circle is pi*r^2 during its entire
existence, then it's area should also be pi*r^2 when it is being created
or destroyed.

I'm not sure what you mean by "wrong output".

You gave an incomplete example. From the output you asserted I and one other
guessed what the missing parts would have to be. That seems to indicate that it
isn't hard at all to relate C++ code to effect and vice versa, re these issues.

Regarding values of member variables or sub-objects, they simply do not exist
before creation or after destruction.


And most programmers, unaware of this little gotcha,
would say the same thing. You, being a C++ proponent and expert, know
differently, but most programmers prefer consistent behavior.

The behavior in C++ is fully consistent.

It would be erronous to invoke methods on a chunk of memory that doesn't satisfy
the assumptions of the methods.

That's what happens in e.g. Java.

And so during T construction and destruction the most derived class, the
object's dynamic type, is T. That's because during construction, any more
derived type's subobject has not yet been initialized, and so doesn't formally
exist, it's just raw memory. And during destruction, any more derived type's
subobject has already been destroyed, hence doesn't formally exist any more.

So the C++ rule is extremely simple: at any time a virtual call invokes the most
derived class' implementation of the method.

The calls are virtual.

And they go to the correct place at any time.



Yes, I'll
grant that programmers also want safety, but it is entirely possible for
a compiler to safely allow virtual methods during construction and
destruction.

C++ does allow virtual calls during construction and destruction.

What's more, C++ actively supports such calls.

It's possible that you have some misconception that calls are not virtual during
construction and destruction, but they are. The only thing you have to keep in
mind is what exist at these point. Happily C++ won't let you do the Wrong Thing.

Post-construction and pre-destruction is however a bit hard to arrange in C++,
and some people, in particular Andrei Alexandrescu, have argued that it should
be made much easier and automated (e.g. you get into this issue with certain
GUIs like X11).

I'm not sure about that. I think making it much easier would lead to it being
employed for general two-phase construction and destruction by those who tend to
choose the short time-frame easy way. Sort of like making goto easier.

[2] Do you know of a way to change the order in which constructors /
destructors are called? I don't, outside of writing a new compiler.

Constructors and destructors are called in a very well defined sequence, except
for what's known as the "static initialization fiasco".

Apart from that issue you specify the construction/destruction order by
declaration order.

When that doesn't suffice you use dynamic allocation.

If you have any particular example/problem, why don't you post it.

[3] I guess "never" is a strong word, so I can meet you half way on this
statement, I suppose, and just say "don't expect virtual methods to work
correctly when called from a constructor or destructor, so call them
there only if you want incorrect behavior." Seems like a silly caveat.
If you feel that the behavior that I describe isn't "incorrect," then
you're too tolerant in your definition of correctness.

It's not a question of feeling.

It's incorrect to invoke methods on non-existent objects.

The behavior you crave, undefined behavior, is simply incorrect by any
reasonable measure of correctness.

[4] My compiler doesn't give me any warning when I call a virtual method
from a constructor or destructor. I don't know what more to tell you.

The compiler shouldn't warn you. You're not doing anything wrong (at least as
you describe it!). It's well-defined in C++.


Cheers & hth.,

- Alf
 
H

Howard Beale

Alf said:
[1] As in the example I gave (and any example that could be given),
you wouldn't know, by just looking at the code, that you're going to
get the wrong output. You would have to also know about the order in
which constructors / destructors are called, which is exactly what is
at issue here. You may say that the output isn't "wrong," but I
personally believe that if the area of a circle is pi*r^2 during its
entire existence, then it's area should also be pi*r^2 when it is
being created or destroyed.

I'm not sure what you mean by "wrong output".

You gave an incomplete example.

It was complete enough to make the point -- in fact, the example wasn't
even needed to make the point. The statement *is* the point:

C++ gives us the notion of "objects" that mimic objects in the real,
physical world. I just want a circle to always act like a circle and a
square to always act like a square without having to hack around
arbitrary decisions that were made by someone else. If you don't want
that, fine. You're free to always drive exactly the speed limit, too,
if you feel that the rules invented by someone else are the way that it
must always be.

Regarding values of member variables or sub-objects, they simply do
not exist before creation or after destruction.

Only because that's the way the standard says it must be done, which,
*again*, is the point I've been making from the start. The fact that
constructors and destructors even exist is just a choice made in the
standard.

So the C++ rule is extremely simple: at any time a virtual call
invokes the most derived class' implementation of the method.

We all know the rule, it's been stated a dozen times here. I believe I
was the first to point it out using the FAQ plus an additional article
that you called FUD. No one needs to have the rule explained any more.

[2] Do you know of a way to change the order in which constructors /
destructors are called? I don't, outside of writing a new compiler.

Constructors and destructors are called in a very well defined
sequence, except for what's known as the "static initialization
fiasco".

I refuse to believe that this far into this discussion, you really need
me to explain that I'm talking about constructors and destructors within
a class hierarchy, and not construction of unrelated global objects.
Come on.

If you have any particular example/problem, why don't you post it.

I stated the problem multiple times, gave an example, and explained
exactly what it is about the behavior that I dislike. I'm not the only
programmer who dislikes it. If you don't want to talk about it, then
just say "I don't want to talk about it" or just stop posting. There is
no need to defend your knowledge on the topic. No one has questioned it
or even disagreed over what the rules of C++ are.


It's not a question of feeling.

Yes, it is. C++ was created by people who felt that the compiler should
behave in a certain way. If they had felt instead that virtual methods
should behave the same way during construction and destruction as they
do at all other times, then they could have written the specification
that way. Yes, the way the behavior is currently specified is
consistent. So what? ANSI could also say that all functions must
always return zero. That's very consistent behavior and if that was the
rule, then it would be "expected" and no one would be surprised by it.
But if someone came along and said "it might be nice for functions to
return non-zero values," would you just quote the spec and say that it's
consistent and expected like you're doing now? Would it matter that it
really *would* be nice for functions to return non-zero values?

The matter of what C++ actually *does* is not a question of feeling, as
you have pointed out over and over again. Please don't point it out
again. It is not being debated here, it never was, it doesn't need to
be.

[4] My compiler doesn't give me any warning...

The compiler shouldn't warn you. You're not doing anything wrong (at
least as you describe it!). It's well-defined in C++.

No, I'm not doing anything wrong, but C++ is.

There are lots of warnings that are given when nothing is wrong, just to
point out that you may get some unexpected behavior if you're not
careful, even though the behavior is well-defined.
 
B

Balog Pal

Howard Beale said:
C++ gives us the notion of "objects" that mimic objects in the real,
physical world. I just want a circle to always act like a circle and a
square to always act like a square without having to hack around
arbitrary decisions that were made by someone else.

And I guess you also expect a house to be created that way too, jumping out
of thin air complete. In reality it is also constructed and in that period
it stays around without roof, walls, windows, etc.
If you don't want that, fine.

Life rarely considers what we want. :-o We have what we have. A
circle-under-counstruction is NOT a circle-complete. Same goes for
circle-being-destroyed. If it is drawn on paper, you can see the same
thing.
You're free to always drive exactly the speed limit, too,
if you feel that the rules invented by someone else are the way that it
must always be.

Or you can go and vore the value of pi into a law as some bunch of
politicians did. O,O

But really, what would be the alternative of construction? I observed it as
a fundamental approach. SICP suggests the same.
I stated the problem multiple times, gave an example, and explained
exactly what it is about the behavior that I dislike. I'm not the only
programmer who dislikes it.

Ok, we all dislike it. Really. But what should be the alternative? Which
solution we would NOT dislike?

Doing the "intended thing" would require telepathy. No less. It would
require to select some aprt of code of the base destructor and run it
sometimes during the derived destructor's body executes. In general there
is no way for the compiler to figure out which part to take and where to put
it.

Another alternative is to forbid virtual calls outside the object's
lifetime.
- making it compile-time error, just by appearance
-- that needs a completely different build model than C++ has. The
compiler should create a full call tree from dtor, and see all the executed
code. Say goodbye to modules, and ability to implement functions in a
different source file. Also that invlaidates code that would call the
virtual on different branches than actually called from the dtor. (similar
problem as nothrow-check of dtors...)

- making it runtime error
-- so insert special instrumentation code just to check for the
possibility; that would likely hit normal performance. Also I doubt a
runtime error would make too many people happy.

And though I don't know any reasonable use of such call, so think it rare,
there may be cases where people use it by the current behavior, or wanted
exactly that way -- forbidding would hurt them.


The decision made for C++ design looks like a fair compromise. What would
YOU do instead?
[4] My compiler doesn't give me any warning...

The compiler shouldn't warn you. You're not doing anything wrong (at
least as you describe it!). It's well-defined in C++.

No, I'm not doing anything wrong, but C++ is.

There are lots of warnings that are given when nothing is wrong, just to
point out that you may get some unexpected behavior if you're not
careful, even though the behavior is well-defined.

For the reasons just said, the compiler can't give you a consistent warning.
But there are static code analysis tools (like Coverity) that I'd bet can
give you that very warning. Don't ask the price in time or money though.

It could flag some blatant cases, but if the more elaborate ones are missed
that could easily create a false-security situation.

TANSTAAFL
 
B

Bo Persson

Balog said:
And doing so you flushed the baby with the bathwater. FUD is
something that puts down a claim and either not include rationale
at all or creates a phony one. You shouldn't address content such
without reading it.

It *IS* a problem. One you appear to miss entirely, or turn a
blind eye.
The problem is not of the nature you argue against. Technically
the call works, and what it does is fully defined. And what
happens (IMO) makes more sense too, than say in java.

The problem is a human one -- when calls to virtuals are done,
directly or indirectly, the expectation is they end up in the most
derived object. In the real-life and not the technical sense.
People are (appear) just not aware that in ctor and dtor different
rules apply, and expect the code just work by magic -- as it does
in every other context.

Why, oh why, should the base class constructor be bothered to call
code in the derived class?

That is the responsiblity of the derived class' constructor, which
will no doubt be executed a microsecond later.
The thing is on the FAQ is exactly for that: too many tried and got
surprized. Or even call 'foul' for calling the 'wrong' function.
The fact that we know tha language rules, and can apply sense to
say what is the 'right' function technicly will NOT make it correct
in the code -- that *expects* a different behavior. That
expectation will not go away by deducing it as like-impossible to
implement either.

In java the manifest problem is dealing with half-baked object in
the expected function.
In C++ it manifests by arriving in Self::foo(). What is way easier
to discover and rearrange the design.

The usual rearrangement is to pass the work up, and make the result
of the former virtual call a parameter of the ctor...
And for dtors you sigh, and copy the code in the derived dtors...

Why? Why can't the destructor of the derived class take care of it's
own destruction?

That is its responsibility!


Bo Persson
 
B

Balog Pal

Bo Persson said:
Why? Why can't the destructor of the derived class take care of it's own
destruction?

That is its responsibility!

Should be obvious. Having class D : public D the order of stuff in
construction is:

1. construct B members
2. execute B body
3. construct D members
4. execute D body

destruction is the mirror.

You call virtual function sometime during (2). And expect it to work in an
environment that would exist at (4). That could only happen if the code part
in question was actually executed magically interleaved with other things in
(4).
 
H

Howard Beale

Balog said:
Life rarely considers what we want. :-o We have what we have. A
circle-under-counstruction is NOT a circle-complete. Same goes for
circle-being-destroyed. If it is drawn on paper, you can see the same
thing.

Good point. We're talking about a problem with OOP in general. And
maybe the answer is that OOP isn't "what we want." We're talking about
defining a programming language -- made by us, made for us, so "what we
want" really does matter in this case. You're basically saying "we
invented C++ and it didn't pan out as well as we might have hoped, so
we're stuck with it."

But really, what would be the alternative of construction? I observed
it as a fundamental approach. SICP suggests the same.

There are plenty of languages (paradigms) that don't invlove
constructors. C, which is where this discussion started, is a great
example.

Ok, we all dislike it. Really. But what should be the alternative?
Which solution we would NOT dislike?

Well, that's why we all come to usenet. To battle trolls and once in a
blue moon, answer a worthwhile question like the one you just re-asked.

Doing the "intended thing" would require telepathy. No less. It would
require to select some aprt of code of the base destructor and run it
sometimes during the derived destructor's body executes. In general
there is no way for the compiler to figure out which part to take and
where to put it.

Not really... You could leave it up to the programmer to explicitly
decide the order of construction. If they screw things up and mess
around with uninitialized memory, they get what they paid for. But at
least the decision would be the programmer's, not the compiler's.

Another alternative is to forbid virtual calls outside the object's
lifetime.
- making it compile-time error, just by appearance
- making it runtime error

These are possibilities... They feel a bit like band-aids, though, and
have drawbacks as you point out.

The decision made for C++ design looks like a fair compromise. What
would YOU do instead?

I do have an idea that I think is rather clever. It addresses all the
issues that I originally pointed out (class hierarchies, templates,
operator overloading), and not just the one subtlety of the "virtual"
keyword that drew so much attention in this thread.

I could go into it, but the horse feels a bit dead at this point.
Besides, I'm just another troll with an idea for creating his own
language. If anyone really needs to know about my fabulous idea, I
could go over it, but I'm equally fine with shutting up about it at this
point.
 
P

Pavel

Bo said:
Why, oh why, should the base class constructor be bothered to call
code in the derived class?
Because

1. It is what the user wants and the compiler has enough information to
generate this code and thereby satisfy the user (the information about
object of which most derived class is actually being created is known at
compile time). Note: if what the user wants is to call the base class's
virtual function, s/he can always force it by explicit qualification.

2. It allows more efficient implementation (the known-to-me C++
implementations first write a pointer to virtual table of the base class
to the object; then override it with the pointer to derived class's
table; this is unnecessary and unjustified overhead breaking the promise
of C++ to be as efficient as possible)

3. The promise of C++ to be as powerful and dangerous to allow the
programmer to blow up his/her entire leg is broken.

4. It is proven to work (by Java for example).
 
K

Krice

We're talking about a problem with OOP in general.

There is no other problem than people who suck as
programmers. Some of them learn to become better programmers,
others blame the programming language/paradigm.
 
A

Alf P. Steinbach

* Pavel:
Because

1. It is what the user wants and the compiler has enough information to
generate this code and thereby satisfy the user (the information about
object of which most derived class is actually being created is known at
compile time). Note: if what the user wants is to call the base class's
virtual function, s/he can always force it by explicit qualification.

Incorrect.

Consider in class T construtor a call to a base class' foo which calls virtual
bar which is overridden in T.

2. It allows more efficient implementation (the known-to-me C++
implementations first write a pointer to virtual table of the base class
to the object; then override it with the pointer to derived class's
table; this is unnecessary and unjustified overhead breaking the promise
of C++ to be as efficient as possible)

Yes, efficiency can be improved.

3. The promise of C++ to be as powerful and dangerous to allow the
programmer to blow up his/her entire leg is broken.

No, you can do whatever you want. But if you want to do nonsensical
initialization you'll have to do it yourself. Not via the language's mechanisms.

4. It is proven to work (by Java for example).

Meaningless unless you define "work". Java programs generally have problems here.


Cheers & hth.,

- Alf
 
P

Pavel

Alf said:
* Pavel:

Incorrect.

Consider in class T construtor a call to a base class' foo which calls
virtual bar which is overridden in T.
I am not sure I am following your sentence above. Did you mean this?

class BT { public:
void foo() { bar(); }
virtual void bar() { cout << "BT::bar()\n"; }
};

class T {
T() { foo(); }
};

This does not present a problem in either specs (existing or desired) as
the derived class' bar() is called in both cases. If you meant anything
different could you please show sample code?
Yes, efficiency can be improved.



No, you can do whatever you want.
Thanks! I guess I did not know that before.
> But if you want to do nonsensical
initialization you'll have to do it yourself. Not via the language's
mechanisms.
You can do whatever you want, too, in particular boldly business
requirements "nonsensical". Fortunately for both of us, I am not your
employer.
Meaningless unless you define "work". Java programs generally have
problems here.
Fair enough. I define "Work" as "satisfy requirements" and "work well"
as "work at lesser cost than known alternatives". IMHO this aspect of
initialization (calling virtuals from constructors) works well in Java
and not so well in C++. Does it make more sense now?
 
J

Joshua Maurice

[1] As in the example I gave (and any example that could be given), you
wouldn't know, by just looking at the code, that you're going to get the
wrong output.  You would have to also know about the order in which
constructors / destructors are called, which is exactly what is at issue
here.  You may say that the output isn't "wrong," but I personally
believe that if the area of a circle is pi*r^2 during its entire
existence, then it's area should also be pi*r^2 when it is being created
or destroyed.  And most programmers, unaware of this little gotcha,
would say the same thing.  You, being a C++ proponent and expert, know
differently, but most programmers prefer consistent behavior.  Yes, I'll
grant that programmers also want safety, but it is entirely possible for
a compiler to safely allow virtual methods during construction and
destruction.

This sounds like the "wrong" position of the circle vs ellipse
problem. Just because in math "all circles are ellipses" does not mean
that "circle should be a subclass of ellipse". It doesn't matter if
the lack of inheritance will surprise "most programmers". In this
case, "most programmers" are wrong, and they will be making a bad
design decision if they went with it.

The writers of the standard, when they wrote the standard, decided to
try and make it difficult to shoot yourself in the foot. Generally,
when one shoots themself in the foot, it's by accident, and generally
by incompetence.

Put another way, "truth" is not democratic. Just because most
programmers think that a circle should "be an" ellipse does not make
it good code.

As another example, what about those programmers who find it
surprising that they can't access an object after it's deleted? Does
that mean manually memory management is bad and we should all be in a
garbage collected environment? Perhaps we should change POSIX as well
to support common anti-patterns, like double checked locking.

Virtual calls made in destructors and constructors do not go down to
the unconstructed most derived object because this would be a logical
error in nearly all cases. It seems as though you want such calls to
act on unconstructed objects, and in which case you are simply wrong,
as based upon years of evidence.
[2] Do you know of a way to change the order in which constructors /
destructors are called?  I don't, outside of writing a new compiler.

The order of construction of subobjects is designed as "syntactic
sugar". All of this you could do in C, but it would be a huge pain to
write out and maintain. Thus, the standard writers decided on the most
useful ordering. Frankly, it's quite a good ordering. They decided to
write a "syntactic sugar" which works really well for 99% of cases. If
you're really in that 1% of cases, you can still write out whatever
manually. However, I don't think you actually hit such a case, and are
instead complaining out of your ass that it doesn't do X when no one
actually needs X. (Where to be very clear, X is the order of
construction and destruction. Thus far, you haven't seemed to disagree
with this, just how virtual functions work in them.)
[4] My compiler doesn't give me any warning when I call a virtual method
from a constructor or destructor.  I don't know what more to tell you.

Yes. I agree that Quality of Implementation issues, like decent
warnings, is a huge problem with modern C++ implementations. (For
example, it annoys me to no end that I cannot find an option on \any\
compiler to flag deleting an incomplete type as a fatal error, or to
flag deleting a void pointer as a fatal error. Personally, I think
it's a shortcoming in the standard and implementations. Both errors
should require a diagnostic, and apart from a requirement from the
standard, any nonshitty compiler should have an option to flag both as
a fatal error turned on by default. I've spent days tracking down
problems from such things.)
 
J

Joshua Maurice

After reading more, I'm still not quite clear on what Howard Beale
want, so I ask again. More clearly: what is wrong with C++, its order
of construction and destruction, and how virtual calls interact.

Let's first presuppose that constructors and destructors themselves
are not broken. Correct me if you think they are.

That leaves us with this heated, and quite unclear, discussion about
which function should be called when you call a virtual function in a
constructor or destructor. I think our options are:

1- Call function on uninitialized (or destroyed) derived class. This
will almost certainly be bad, and not what the programmer intended (or
the programmer is not familiar with the language and good code design,
and the design is bad).

2- What is currently done. It goes to the most derived currently
constructed object.

3- Disallow virtual function calls on objects under construction or
destruction.

I think I like #2 the most, though I can see some utility from #3.
Howard, I still think you are arguing for #1, and I strongly disagree
with that option on my knowledge and years of experience.
 
A

Alf P. Steinbach

* Pavel:
I am not sure I am following your sentence above. Did you mean this?

class BT { public:
void foo() { bar(); }
virtual void bar() { cout << "BT::bar()\n"; }
};

class T {
T() { foo(); }
};

This does not present a problem in either specs (existing or desired) as
the derived class' bar() is called in both cases. If you meant anything
different could you please show sample code?

class BT
{
private:
virtual void bar() { say( "BT" ); }
public:
void foo() { bar(); }
};

class T
: public BT
{
private:
virtual void bar() { say( "T" ); }
public:
T() { foo(); }
};

class D
: public T
{
private:
std::string myPavelonian;
virtual void bar() { say( myPavelonian.c_str(); }
public:
D(): myPavelonian( "D" ) {}
};

int main() { D(); }

Works well with C++ rules, calling T::bar as the T programmer intended.

Undefined behavior with Pavel/Howard rules.

Contrary to your claim there's no way to use explicit qualification in class T
or in class BT to make it call T::bar with Pavel/Howard rules.

Thanks! I guess I did not know that before.

You can do whatever you want, too, in particular boldly business
requirements "nonsensical".

Yes, it would be bold to require undefined behavior.


Hth.,

- Alf
 
B

Bo Persson

Pavel said:
Because

1. It is what the user wants and the compiler has enough
information to generate this code and thereby satisfy the user (the
information about object of which most derived class is actually
being created is known at compile time). Note: if what the user
wants is to call the base class's virtual function, s/he can always
force it by explicit qualification.

You shouldn't always give people what they believe they want. :)

Calling a virtual function on a non-existing object is one of those
things. Here C++ avoids undefined behavior by defining that you can
only call the function for the object that actually exists at that
point.
2. It allows more efficient implementation (the known-to-me C++
implementations first write a pointer to virtual table of the base
class to the object; then override it with the pointer to derived
class's table; this is unnecessary and unjustified overhead
breaking the promise of C++ to be as efficient as possible)

There is an extra cost for this, true.
3. The promise of C++ to be as powerful and dangerous to allow the
programmer to blow up his/her entire leg is broken.

My impression is that C++ tries to avoid the shoot-in-the-foot
situation whenever possible (and without a run-time cost :). This
does leave some opportunities for blowing a leg off, for the
adventurous programmer.
4. It is proven to work (by Java for example).

For some definition of work. I'm no Java expert, but from what I
understand the cost you want to avoid in 2) now moves to a test for a
fully constructed object in every call to the function.

My point is that the derived object should fully construct itself, and
not depend on the base object calling some function during its
construction. It is about the distribution of responsibilities.


Bo Persson
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top