Java Forum / General / June 2007
Virtual function call from constructor
Daniel Kraft - 23 Jun 2007 12:38 GMT Hi all,
I came across some unexpected behaviour in Java: When I call a member-function from a class' constructor, this call is done virtual as usual calling the overridden method of the subclass.
This seems to me a bit problematic, as it calls a member-function of the subclass which is not yet constructed and might therefore be well in an inconsistent state. One can even call an abstract function from a class' constructor.
In C++, this is the other way round, there a constructor never calls an overridden method from a subclass, so this inconsitent state can never occur, which seems to me to be the better way. Calling a pure virtual function from a constructor yields at least a warning and of course a linker-error later on.
Is there any rationale behind this as it is done in Java? The only reason I can think of is to handle function call's in constructors the same way as it is done with ordinary calls. But the function to call there is would be known at compile-time, so it shouldn't be really hard to implement C++'s behaviour.
Or is this even considered "a feature"? However, I believe a function should *never* be called on an unconstructed object except in the constructor of the class itself where the programmer must take care this call is valid there.
Cheers, Daniel
****************************** Test.java
public class Test {
public Test() { foo(); }
public void foo() { System.out.println("A"); }
public static void main(String[] args) { Test t=new Test2(); }
public static class Test2 extends Test {
public Test2() { super(); foo(); }
public void foo() { System.out.println("B"); }
}
}
This produces: B B
******************************** Test.cpp
#include <iostream> using namespace std;
class Test { public: Test() { foo(); } virtual void foo() { cout << "A" << endl; } };
class Test2 : public Test { public: Test2() : Test() { foo(); } virtual void foo() { cout << "B" << endl; } };
int main() { Test* t(new Test2()); delete t; return 0; }
This produces: A B
 Signature Got two Dear-Daniel-Instant Messages by MSN, associate ICQ with stress -- so please use good, old E-MAIL!
Jeff Higgins - 23 Jun 2007 13:04 GMT This prints AB?
public class Test { Test() { foo(); }
static void foo() { System.out.print("A"); }
public static void main(String[] args) { Test t = new Test2(); }
static class Test2 extends Test { Test2() { super(); foo(); }
static void foo() { System.out.print("B"); } } }
Daniel Kraft - 23 Jun 2007 13:07 GMT > This prints AB? Try it out, for me it does.
> public class Test > { [quoted text clipped - 27 lines] > } > }
 Signature Got two Dear-Daniel-Instant Messages by MSN, associate ICQ with stress -- so please use good, old E-MAIL!
Eric Sosman - 23 Jun 2007 13:06 GMT > Hi all, > [quoted text clipped - 6 lines] > inconsistent state. One can even call an abstract function from a > class' constructor. ... which is why every Java text warns you: "Don't Do That."
> In C++, this is the other way round, there a constructor never calls an > overridden method from a subclass, so this inconsitent state can never [quoted text clipped - 7 lines] > there is would be known at compile-time, so it shouldn't be really hard > to implement C++'s behaviour. Suppose the class has a non-overridable method (private, final, or static) that performs some useful operation on an object. And suppose this method uses overridable methods of the object. Do you need to compile two variants of this method to make it behave differently depending on whether there is or isn't a constructor somewhere in its call stack? (In fact, "a constructor" isn't enough, not even "a constructor of the same class." If I've got a fully-constructed object A lying around and I call a method on it while constructing another object B, I want that method to obey the overrides when applied to object A but not to B. I'm not sure it's even possible to sort that out entirely at compile time. I think you'd need a "construction finished" flag on every object -- in fact, one such flag for every level of inheritance starting from Object itself. "Don't Do That" seems a lot simpler ...)
 Signature Eric Sosman esosman@acm-dot-org.invalid
Daniel Kraft - 23 Jun 2007 13:37 GMT > Suppose the class has a non-overridable method (private, final, > or static) that performs some useful operation on an object. And [quoted text clipped - 10 lines] > in fact, one such flag for every level of inheritance starting > from Object itself. "Don't Do That" seems a lot simpler ...) Hm, that's a good point! I believe in C++ this is done by updating the vtable for each constructor finished or something like this -- that should also be fairly simple to implement and you do not need a "construction finished" flag, but of couse it imposes some costs.
"Don't do it" is of course nice, but precisly it should mean "only call private, final and static methods from a constructor and only if they themselves obey this rule", right?
But in this case, wouldn't a compile-time waring be useful telling me I'm calling an overridable method from my constructor or the like?
Yours, Daniel
 Signature Got two Dear-Daniel-Instant Messages by MSN, associate ICQ with stress -- so please use good, old E-MAIL!
Eric Sosman - 23 Jun 2007 14:20 GMT >> Suppose the class has a non-overridable method (private, final, >> or static) that performs some useful operation on an object. And [quoted text clipped - 15 lines] > should also be fairly simple to implement and you do not need a > "construction finished" flag, but of couse it imposes some costs. I don't know how C++ is implemented, but if the "vtable" is a bunch of pointers to methods it sounds like you'd need a per-instance edition of it to reflect the instance's fully- or partially-constructed state. Sounds like a lot of bloat ...
> "Don't do it" is of course nice, but precisly it should mean "only call > private, final and static methods from a constructor and only if they > themselves obey this rule", right? Right.
> But in this case, wouldn't a compile-time waring be useful telling me > I'm calling an overridable method from my constructor or the like? I suppose so, but I don't think the compiler would be able to detect every path that could lead to a Don't Do That call. Some of them might not originate in the constructor at all: for example, the constructor might insert the half-baked object in a collection where another thread could find it before the constructor has finished its job.
 Signature Eric Sosman esosman@acm-dot-org.invalid
Daniel Kraft - 23 Jun 2007 15:12 GMT >> Hm, that's a good point! I believe in C++ this is done by updating >> the vtable for each constructor finished or something like this -- [quoted text clipped - 5 lines] > per-instance edition of it to reflect the instance's fully- > or partially-constructed state. Sounds like a lot of bloat ... Right, the vtable (or virtual table) is a table of function pointers stored within each object so the overridden method is found. But I really believe this is there in Java, too, but in Java of course the fact of methods being virtual is "hidden" to the programmer.
So yes, this is some bloat, but AFAIK this bloat is really necessary for polymorphism with virtual methods. And unlike Java, in C++ you do not have to keep a pointer there for *every* method a class has, only to those which you really intend to be overridden and used polymorphistic...
Cheers, Daniel
 Signature Got two Dear-Daniel-Instant Messages by MSN, associate ICQ with stress -- so please use good, old E-MAIL!
Lew - 23 Jun 2007 15:41 GMT > Right, the vtable (or virtual table) is a table of function pointers > stored within each object so the overridden method is found. But I [quoted text clipped - 5 lines] > have to keep a pointer there for *every* method a class has, only to > those which you really intend to be overridden and used polymorphistic... So don't use Java, then. Do your work in C++.
What do you mean by "you ... have to keep a pointer"? Are you certain that Java doesn't inline non-polymorphic methods, or even polymorphic ones? How is "keeping a pointer" less worthy than not doing so? In any event, vtable or not, threaded or pointer, has literally nothing to do with Java semantics, which are defined irrespective of their implementation.
It seems to me that the illogic of a method only being polymorphic outside a constructor but, inconsistently, not inside it, trumps the apparent illogic but actual good sense of polymorphic (or otherwise) methods only seeing fully-constructed objects after they're fully constructed.
 Signature Lew
Mike Schilling - 24 Jun 2007 04:16 GMT ...
> Right, the vtable (or virtual table) is a table of function pointers > stored within each object so the overridden method is found. But I really > believe this is there in Java, too, but in Java of course the fact of > methods being virtual is "hidden" to the programmer. Not at all. In Java, private methods aren't virtual and all other methods are, and this is understood by every competent Java programmer. And the vtable in C++ is part of the implementation: it's not something the programmer can access [1], and thus is equally "hidden".
> So yes, this is some bloat, No, there isn't. See my explanation upthread.
>but AFAIK this bloat is really necessary for polymorphism with virtual >methods. And unlike Java, in C++ you do not have to keep a pointer there >for *every* method a class has, only to those which you really intend to be >overridden and used polymorphistic.. The inner workings of the JVM are very hidden. There's no reason to think that if a method is never overridden that the JVM can't dispatch to a method directly.
1. OK, you can access it using casts and pointer arithmetic, in an implementation-specific way. I've done this, and even for good reasons. But it's not part of the language definition.
Mike Schilling - 24 Jun 2007 04:09 GMT >>> Suppose the class has a non-overridable method (private, final, >>> or static) that performs some useful operation on an object. And [quoted text clipped - 15 lines] >> should also be fairly simple to implement and you do not need a >> "construction finished" flag, but of couse it imposes some costs. You are correct.
> I don't know how C++ is implemented, but if the "vtable" > is a bunch of pointers to methods it sounds like you'd need a > per-instance edition of it to reflect the instance's fully- > or partially-constructed state. Sounds like a lot of bloat ... Not per-instance, per-class. Assume S2 extends S1, and S1 extends S0. An Sn constructor:
calls the superclass constructor (if there is a superclass) points its vtable at the Sn version runs its code
The result is that the constructor runs as if the object were the type being constructed, and when all have run, the object is of the correct (most-derived) type. No bloat involved. I always find it silly when people who know nothing about a language assume that if it does things differently from the languages they're used to, it must be stupid and inefficient to do so.
Calling virtual methods (directly or indirectly) in a constructor is problematical. C++ tries to solve it by limiting the way they're called. Java makes avoiding problems wholly the programmer's responsibility. Neither is stupid or inefficient; both have advantages and disadvantages. (Ironically, the difference is opposite to the usual one, since C++ is being nannyish and Java is giving you all the rope you want.)
Eric Sosman - 24 Jun 2007 14:45 GMT Mike Schilling wrote:
>>>> Suppose the class has a non-overridable method (private, final, >>>> or static) that performs some useful operation on an object. And [quoted text clipped - 34 lines] > from the languages they're used to, it must be stupid and inefficient to do > so. Somehow I'm failing to find the word "stupid" or "inefficient" in anything that I wrote. Could you help me with my proofreading, please?
I *did* use the word "bloat," and it still seems justifiable. The implementation you describe (if I've understood you correctly) requires each instance to carry a pointer per inheritance level; that's certainly larger than the one bit per level I described as a hypothetical implementation, and that in turn is larger than no per-instance "construction status" at all.
 Signature Eric Sosman esosman@acm-dot-org.invalid
Mike Schilling - 24 Jun 2007 16:56 GMT > I *did* use the word "bloat," and it still seems justifiable. > The implementation you describe (if I've understood you correctly) > requires each instance to carry a pointer per inheritance level; > that's certainly larger than the one bit per level I described as > a hypothetical implementation, and that in turn is larger than > no per-instance "construction status" at all. Not at all. It requires each instance to hold one pointer, period. The *value* of this pointer is changed during construction.
Eric Sosman - 24 Jun 2007 22:35 GMT >> I *did* use the word "bloat," and it still seems justifiable. >> The implementation you describe (if I've understood you correctly) [quoted text clipped - 5 lines] > Not at all. It requires each instance to hold one pointer, period. The > *value* of this pointer is changed during construction. Aha! So if you've got a class that's N levels deep in the inheritance hierarchy, you've got N+1 versions of the vtable, each corresponding to a different amount of progress through the constructor chain. Is that it?
Hmmm... If that's the way it works, then defining an Nth level class really defines N+1 distinct classes, in the sense that a class is the set of its behaviors. If applied to an object in the midst of construction, which of those N+1 classes should getClass() return? And how should instanceof behave?
 Signature Eric Sosman esosman@acm-dot-org.invalid
Mike Schilling - 24 Jun 2007 23:02 GMT >>> I *did* use the word "bloat," and it still seems justifiable. >>> The implementation you describe (if I've understood you correctly) [quoted text clipped - 10 lines] > each corresponding to a different amount of progress through > the constructor chain. Is that it? Each class that contains virtual functions defines a vtable. Each instance of one of these classes contains one pointer, which points to its class's vtable. So an instance of String points to String's vtable, an instance of Thread points to Thread's vtable,etc. So far, just as you'd expect. If you replace "vtable" by "Class object" and observe that all Java classes contain virtual functions (the ones they inherit from Object, at least), exactly like Java.
What C++ does differently is this: During construction, the value of that vtable pointer can changes. It will always point to the vtable for the class that defines the constructor. So (to use a Java-like example, for familiarity), when a PrintStream is being constructed, it does something ,like this:
Point to Object vtable Run Object constructor Point to OutputStream vtable Run OutputStream constructor Point to FilterOutputStream vtable Run FilterOutputStream constructor Point to PrintStream vtable Run PrintStream constructor
At the end of this, the vtable is (correctly) left pointing to the PrintStream vtable, which it keeps for the rest of its left. It's possible that the reverse process happens during destruction, but I can't say for sure. If so, it would be nicely symmetrical.
> Hmmm... If that's the way it works, then defining an Nth > level class really defines N+1 distinct classes, in the sense > that a class is the set of its behaviors. Not really, because the superclasses already exist. You're defining only one new class.
> If applied to an > object in the midst of construction, which of those N+1 classes > should getClass() return? And how should instanceof behave? C++ doesn't have either of those per se. It does have some limited reflection, for instance the <dynamic_cast> operator, which does a similar job to instanceof, which I presume would base the current type of the object on the current vtable, so that, in effect,
x instanceof PrintStream
would be false during the running of the FilterOutputStream constructor. Which makes a certain amount of sense: none of the PrintStream fields have been initialized, nor are PrintStream-specific methods available.
Think of it this way. When the Object constructor is running, the instance *is* an Object. It may have some extra space allocated at the end, but no one can make any use of it. Now, when the OutputStream constructor is running, the instance *is* an OutputStream. It may have some extra space allocated at the end, but no one can make any use of it. etc. Finally, when the PrintStream constructor runs, it *is* a PrintStream.
Twisted - 24 Jun 2007 23:41 GMT On Jun 24, 6:02 pm, "Mike Schilling" <mscottschill...@hotmail.com> wrote:
> What C++ does differently is this: During construction, the value of that > vtable pointer can changes. It will always point to the vtable for the > class that defines the constructor. Think of it this way: while a Java object is whatever run-time type it is for its whole lifetime, a C++ object actually evolves; its run-time type is Base during the Base constructor, then Derived when the Derived constructor is running. Its run-time type is whatever is fully- constructed so far.
> > Hmmm... If that's the way it works, then defining an Nth > > level class really defines N+1 distinct classes, in the sense > > that a class is the set of its behaviors. > > Not really, because the superclasses already exist. You're defining only > one new class. For example with Base and Derived, there may be a trivial vtable and there will be ones for Base and Derived. But the one for Base is used not only for incomplete Deriveds, but for other subclasses and vanilla Bases as well.
> C++ doesn't have either of those per se. It does have some limited > reflection, for instance the <dynamic_cast> operator, which does a similar > job to instanceof... Actually, it does the exact same job as a Java cast as in "(String) myMap.get(key)" (a common case, pre-Java 5). It gives you a reference of the desired compile-time type if the run-time type is assignable to that compile-type type, and throws an exception if it isn't so assignable. In Java the exception is a ClassCastException; in C++ it's something else (bad_cast?) (C++ also has bad_alloc, analogous to OutOfMemoryError, and some other exceptions that closely map onto Java ones.)
> which I presume would base the current type of the > object on the current vtable, so that, in effect, [quoted text clipped - 4 lines] > Which makes a certain amount of sense: none of the PrintStream fields have > been initialized, nor are PrintStream-specific methods available. I think there may be an RTTI (run-time type identification) ability more like instanceof than dynamic_cast; I'm not sure if it treats objects' run-time types as evolving during construction, though it seems likely. Dynamic dispatch certainly does, regardless.
> Think of it this way. When the Object constructor is running, the instance > *is* an Object. It may have some extra space allocated at the end, but no > one can make any use of it. Now, when the OutputStream constructor is > running, the instance *is* an OutputStream. It may have some extra space > allocated at the end, but no one can make any use of it. etc. Finally, when > the PrintStream constructor runs, it *is* a PrintStream. Except that in Java it's a PrintStream from the outset, and is not a vanilla Object even when Object's constructor is not finished yet. (Does Object even have a nontrivial constructor?) This only applies to their C++ analogues in the iostreams library. :)
Mike Schilling - 24 Jun 2007 23:57 GMT >> Think of it this way. When the Object constructor is running, the >> instance [quoted text clipped - 9 lines] > vanilla Object even when Object's constructor is not finished yet. > (Does Object even have a nontrivial constructor?) That's presumably a JVM-specific question.
> This only applies to > their C++ analogues in the iostreams library. :) Yes, I was probably unclear. I was trying to describe the C++ mechanism, but using a Java class hierarchy as an example (since there wasn't a C++ hierarchy handy.)
Twisted - 25 Jun 2007 00:37 GMT On Jun 24, 6:57 pm, "Mike Schilling" <mscottschill...@hotmail.com> wrote:
> > Except that in Java it's a PrintStream from the outset, and is not a > > vanilla Object even when Object's constructor is not finished yet. > > (Does Object even have a nontrivial constructor?) > > That's presumably a JVM-specific question. Yes. I expect a JVM and a corresponding implementation of at least the java.lang portion of the standard library to be fairly tightly coupled; probably why they are bundled and transported about as a single entity, a JRE. Certainly some System stuff, Object.wait/notify, and the stuff in java.lang.ref will be tightly coupled to the VM implementation in various ways, and likely also String interning. Class/ClassLoader and reflection too, and now probably java.util.concurrency (and that one doesn't even start with java.lang).
> > This only applies to > > their C++ analogues in the iostreams library. :) > > Yes, I was probably unclear. I was trying to describe the C++ mechanism, > but using a Java class hierarchy as an example (since there wasn't a C++ > hierarchy handy.) I figured as much, but also figured it might not be clear to every reader. Having similar stuff expressed in other words as well will probably make it much more reliably understood by this thread's readers.
Eric Sosman - 25 Jun 2007 02:27 GMT >>> Think of it this way. When the Object constructor is running, the >>> instance [quoted text clipped - 10 lines] > > That's presumably a JVM-specific question. No; that's the Java language. If Object's constructor ultimately chained from a PrintStream constructor were to evaluate `this instanceof PrintStream' the result would be `true', on every JVM. (Object's constructor has no reason to do any such thing, but that's another matter.)
You can test the pattern with
class Super { Super() { System.out.println((this instanceof Super) + ", " + (this instanceof Sub)); } public static void main(String[] unused) { new Super(); new Sub(); } } class Sub extends Super { }
 Signature Eric Sosman esosman@acm-dot-org.invalid
Mike Schilling - 25 Jun 2007 02:55 GMT >>>> Think of it this way. When the Object constructor is running, the >>>> instance [quoted text clipped - 17 lines] > `true', on every JVM. (Object's constructor has no reason > to do any such thing, but that's another matter.) The question was "Does Object even have a nontrivial constructor?" That is, whether its constructor contain any code. As far as I know, that can differ between implementations.
Tom Hawtin - 25 Jun 2007 06:51 GMT > The question was "Does Object even have a nontrivial constructor?" That is, > whether its constructor contain any code. As far as I know, that can differ > between implementations. You can rewrite the Object constructor using instrumentation, so I believe all implementation should allow the Object constructor to contain real Java code.
Tom Hawtin
Twisted - 24 Jun 2007 04:22 GMT > "Don't do it" is of course nice, but precisly it should mean "only call > private, final and static methods from a constructor and only if they > themselves obey this rule", right? Basically. I'd divide my methods into "those that can definitely cope with an incompletely-constructed Foo" and "all the others". The latter including all methods not in Foo, including in Foo's superclass but not overridden by Foo, or overridable by Foo's subclasses. So the first set is already limited to a subset of the final, static, and private methods of Foo. Lastly, the rule I'd have is for the constructor *never to pass "this" to a method not in the first subset*, and to exclude a method from that subset if it ever passes "this" to a method outside the subset.
This includes the implicit "this" argument passed when invoking a member, of course.
The rule that results is "only call private, final and static methods from a constructor and only if they themselves obey this rule" with the additional restriction that this additionally applies to methods of ANY class receiving an explicit argument of "this".
In fact, javac complains in some cases about passing "this" as an argument to a method call from a constructor, which helps avoid some bad potential problems.
Roedy Green - 23 Jun 2007 15:25 GMT >This seems to me a bit problematic, as it calls a member-function of the >subclass which is not yet constructed and might therefore be well in an >inconsistent state. One can even call an abstract function from a >class' constructor. You can get uninitialised variables and other weirdness. Generally don't do it. Avoid calling virtual functions in constructors.
see http://mindprod.com/jgloss/constructor.html point #10. -- Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|