Rationale behind constructor call chain... ( and comparison with C++)

A

A.B.

Just a question...

If an object B extends an object A, whenever B is constructed, A is
constructed... if A() calls a
function that is overloaded in B, it looks like the overloaded function
is called by the A constructor ...

( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

Thanks!
 
T

Thomas Hawtin

A.B. said:
Just a question...

If an object B extends an object A, whenever B is constructed, A is
constructed... if A() calls a
function that is overloaded in B, it looks like the overloaded function
is called by the A constructor ...

I think you mean class B and class A. If B extends A, then an instance
of B is itself an instance of A.

( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

And abort(?) if the method is abstract.
Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

The idea of overriding is that the base classes method is overridden
always. It does cause problems in that you can have a method called
which tries to use fields that have not been initialised yet, but it's
better to fail obviously in development than do something subtly wrong
through to production.

This means that factory methods work from the constructor. For instance
you'll often see in Swing components have protected create methods that
are called from the constructor.

If you really wanted to, you could add an "initialised" boolean
variable. Set it after the super constructor is called. In you
overriding methods, if it has not been set just call the super method
and exit. I'd be interested to see any uses of this, as in nine years of
Java programming I have not come across one.

Tom Hawtin
 
A

andrewmcdonagh

Just a question...

If an object B extends an object A, whenever B is constructed, A is
constructed... if A() calls a
function that is overloaded in B, it looks like the overloaded function
is called by the A constructor ...

( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

Thanks!

The C++ approach is correct for C++.
The Java(and C#) approach is correct for Java(and C#).

The languages deal with calls to virtual methods during constructor
calls in ways that make sense for those languages.

Its not a question over which is a better way.

In any case, its commonly understood to be 'a bad thing' in all 3
languages.

The 'Effective C++ book warns against it', see Chapter 2, Item 9: Never
call virtual functions during construction or destruction. page 48.

The same is true for Java and C# (but I can't google a good source for
you to see).

On the other hand, here's a Java RFI that was rejected....with
reasons...

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4378291

Regards

Andrew
 
E

Eric Sosman

A.B. said:
Just a question...

If an object B extends an object A, whenever B is constructed, A is
constructed... if A() calls a
function that is overloaded in B, it looks like the overloaded function
is called by the A constructor ...

No, that never happens. A's constructor never calls an
overloaded method of class B.

Of course, I'm mocking you a little bit here. A's constructor
might call a method that is over*ridden* by class B, if the coders
of A and/or B were careless. But please: If you're going to debate
fine points of the language, proper use of the existing nomenclature
will be an aid to fruitful discussion.
( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

Can't comment; have avoided C++ with the same unreasoning
hatred that kept me away from COBOL.
Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

At a guess, the rationale is simplicity, aka uniformity. As
things stand, the rules for routing method calls are the same within
constructors as they are everywhere else, and special cases are not
required. If I understand your description of C++ behavior correctly,
it seems there must be a "constructor mode" during which the rules
behave differently. Further, it seems this mode must actually be
dynamic: if method M1 calls method M2 but M1 can be called both
from a constructor and from another post-construction method, is
it supposed to choose A's native M2 during A construction and B's
overriding M2 thereafter? Ay-ay-ay!... If that's how C++ actually
behaves, I think I've been right to avoid it.
 
C

Chris Uppal

A.B. said:
( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

The answer to the last question is no. If you want "special" semantics during
initialisation then you will have to write code to implement the necessary
checks and conditional behaviour.

I think there are two possible reasons why Java didn't follow C++ in this (when
it did borrow a fair amount of other stuff).

One is that it makes the semantics simpler -- there are no special cases to
describe or worry about. Both the C++ semantics and the Java semantics make
some things easier but other things harder. So, given the choice between two
options, both of which have advantages and disadvantages in roughly equal
measure, it seem sensible to choose the simpler and more uniform of the two.

The other is that C++'s semantics are difficult to implement without committing
the language to using a vtable-based implementation of method dispatch. That
is inefficient on most real-world processors. (It would be /possible/ to
implement C++ semantics without vtables, but it'd be complicated to do
efficiently.)

-- chris
 
T

Tor Iver Wilhelmsen

A.B. said:
( In C++ the behavior is always to call the function defined in A when
called within the A constructor, even if the overloaded function is B
is virtual. )

That's because C++ is an ugly hack on top of C. Since C does not "see"
that B::foo() is a candidate for A::foo() until B is compiled and has
its vptr table filled, A's constructor can only call the method it
sees, which is A::foo().

C++ programs run on platforms that lack the virtual call instruction
of the Java VM.
Can someone explain me the rationale behind java's behavior in that
case, how it's useful, more logical etc than the C++ approach... can
this behavior be overriden?

Yes, use a different language, there are hundreds out there. :p

Constructors should ONLY be used for field initialization. If you call
any methods that can be overridden (i.e. anything not declared
private, final or static), you should document this silly side-effect
of yours.
 
T

Thomas Hawtin

Tor said:
That's because C++ is an ugly hack on top of C. Since C does not "see"
that B::foo() is a candidate for A::foo() until B is compiled and has
its vptr table filled, A's constructor can only call the method it
sees, which is A::foo().

I don't quite follow. Any function in A that calls a virtual function on
this (and it isn't in the constructor) will run the overridden version.

A possible explanation is that the C++ -> C bindings used a single
function for the constructor, whether it was called from a subclass
constructor or not. The constructor needs to set the vtable pointer. If
it sets the pointer before calling the base class constructor, then the
base class will just overwrite it. Therefore, the vtable pointer needs
to be written after the base class constructor is invoked, and the
behaviour follows.

In JVM bytecode, creating an instance of a class takes two instructions:
new to allocate and invokespecial to call the most derived class'
constructor. On pre-1.6 JVMs, you could throw an exception before
calling the super constructor, and the finalizer would still run with a
full set of 'virtual' methods (From 1.5 the Object construct must exit
normally in order for the finalizer to run).

I don't recall Stroustrup's The Design and Evolution of C++ mentioning
this issue.
C++ programs run on platforms that lack the virtual call instruction
of the Java VM.

What virtual call instruction? Can you name some platforms?

Tom Hawtin
 
T

Thomas Hawtin

Tor said:
The "invokevirtual" instruction (0xb6) as described in the JVM spec,
section 7.7.

Platforms: Sun's implementation is the most widely used.

Now I'm confused.

If you were talking about the Java platform, what has that got to do
with C++?

You could target the 6502 with a Java bytecode compiler, but that has no
invokevirtual instruction.

Tom Hawtin
 
T

Tor Iver Wilhelmsen

Thomas Hawtin said:
What virtual call instruction? Can you name some platforms?

The "invokevirtual" instruction (0xb6) as described in the JVM spec,
section 7.7.

Platforms: Sun's implementation is the most widely used.
 
E

EJP

Thomas said:
And abort(?) if the method is abstract.

No, give a compilation error because the method is still abstract (C++
pure virtual) at the point of this invocation. Personally I prefer the
C++ behaviour. You shouldn't be able to do anything to a derived class
until the base class is completely constructed.
 
T

Thomas Hawtin

EJP said:
No, give a compilation error because the method is still abstract (C++
pure virtual) at the point of this invocation. Personally I prefer the
C++ behaviour. You shouldn't be able to do anything to a derived class
until the base class is completely constructed.

I believe it's legal C++. A more likely scenario is you call a function
that calls the abstract function.

The Sun Studio 11 C++ compiler gives me a warning (not an error) if I
call a pure virtual function directly. Nothing is reported if I call it
indirectly.

My test program (a.cpp):

class Base {
public:
virtual void fn() = 0;
void gn() {
fn();
}
Base() {
gn();
fn();//
}
};
void Base::fn() {
}
class Derived : Base {
public:
Derived() : Base() {
}
void fn() {
}
};

int main() {
Derived d;
}

Warning message (provide the call to fn is not commented out):

"a.cpp", line 9: Warning: Attempt to call a pure virtual function
Base::fn() will always fail.
"a.cpp", line 9: Warning: Attempt to call a pure virtual function
Base::fn() will always fail.
2 Warning(s) detected.

I'm not sure why the warning is printed twice. Perhaps the compiler
thought it was important.

When run (whether the call to fn is commented out or not, and even with
an implementation of the pure virtual function supplied):

Pure virtual function called
Abort (core dumped)

Tom Hawtin
 
T

Tor Iver Wilhelmsen

Thomas Hawtin said:
If you were talking about the Java platform, what has that got to do
with C++?

It was an example of how the platforms were different. This thread
started with someone complaining that (virtual) method calls in Java
behaved differently from C++. My claim is that this is not a problem
unless you want all languages to behave the same, in which all you
need is one.
You could target the 6502 with a Java bytecode compiler, but that
has no invokevirtual instruction.

Really? All VMs I've seen for that small processors run interpreted,
not byte-compiled.

http://www.mts.net/~kbagnall/commodore/java.html
 
T

Thomas Hawtin

Tor said:
Really? All VMs I've seen for that small processors run interpreted,
not byte-compiled.

http://www.mts.net/~kbagnall/commodore/java.html

YA RLY

There are a number of reasons why you'd want to interpret on a small
platform. Probably the most important is that it requires much less
effort to write an interpreter over a compiler. It has nothing to do
with whether there is a one-to-one correspondence between JVM and
machine instructions.

I've seen a suspicious lack of details about what happens with JavaCard
implementations, although it does seem set up for cross compilation.

So, invokevirtual in 6502 (assuming we're not using "sideways" memory or
anything like that)

; zero page location
obj = 0 ; object to call
tmpA = 2 ; a temporary

; invokevirtual
; JSR to push return code onto stack first
; There is no JSR indirect
; (at least not in classic NMOS Mostek 6502).
JSR invokeCode
...

..invokeCode
; Called with return address on stack.
; We push method address onto stack (so two addresses)
; and RTS to jump to it.

; load vtbl/class from obj
LDY #0
LDA (obj), Y
STA tmpA+0
INY
LDA (obj), Y
STA tmpA+1
; tmpA now points to obj.vtbl

; push method address onto stack
; I think this is the right way around...
; Not sure if vtbl requires a fixed offset from actual address.
; An alternative method is to store and JMP indirect
LDY #methodOffset+1
LDA (tmpA), Y
PHA
DEY
LDA (tmpA), Y
PHA

; not return - jump to method
RTS

6502 was a 1980s thing for me, so I'm a bit rusty.

Actually checking my instructions (I used X for indirect postindexing,
at first), I see using RTS for an indirect jump is a standard idiom.

http://www.atarimax.com/jindroush.atari.org/aopc.html#RTS

Tom Hawtin
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

There are a number of reasons why you'd want to interpret on a small
platform. Probably the most important is that it requires much less
effort to write an interpreter over a compiler. It has nothing to do
with whether there is a one-to-one correspondence between JVM and
machine instructions.

????

http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html#31293

is very clear: JVM's has an invokevirtual instruction for
the Java byte code.

It does not matter whether the JVM interprets that instruction or
JIT compiles it to some native instructions.

The instruction is valid byte code or it is not a JVM.

Arne
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top