19

In Java:

class Base {
    public Base() { System.out.println("Base::Base()"); virt(); }
    void virt()   { System.out.println("Base::virt()"); }
}

class Derived extends Base {
    public Derived() { System.out.println("Derived::Derived()"); virt(); }
    void virt()      { System.out.println("Derived::virt()"); }
}

public class Main {
    public static void main(String[] args) {
        new Derived();
    }
}

This will output

Base::Base()
Derived::virt()
Derived::Derived()
Derived::virt()

However, in C++ the result is different:

Base::Base()
Base::virt() // ← Not Derived::virt()
Derived::Derived()
Derived::virt()

(See http://www.parashift.com/c++-faq-lite/calling-virtuals-from-ctors.html for C++ code)

What causes such a difference between Java and C++? Is it the time when vtable is initialized?

EDIT: I do understand Java and C++ mechanisms. What I want to know is the insights behind this design decision.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Xiao Jia
  • 4,169
  • 2
  • 29
  • 47

7 Answers7

16

Both approaches clearly have disadvatages:

  • In Java, the call goes to a method which cannot use this properly because its members haven’t been initialised yet.
  • In C++, an unintuitive method (i.e. not the one in the derived class) is called if you don’t know how C++ constructs classes.

Why each language does what it does is an open question but both probably claim to be the “safer” option: C++’s way prevents the use of uninitialsed members; Java’s approach allows polymorphic semantics (to some extent) inside a class’ constructor (which is a perfectly valid use-case).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 8
    C++ calling of the base::virtual method is quite intuitive once you accept that C++ instances are constructed like onions (from inside to outside, step by step); so, when base is constructed, there is no overriding of its methods. it happens inly later, when the onion grows... – user1284631 Sep 22 '14 at 21:17
  • Re "both probably claim to be the “safer” option", no I wouldn't think so, since this is reportedly one of the most common causes of bugs in Java. – Cheers and hth. - Alf Jun 06 '16 at 07:25
  • @Cheersandhth.-Alf I can’t give you a source now but I’m sure I’ve read this. I vaguely remember that it was in a book/talk by Josh Bloch but I agree with your assessment and I can’t imagine Josh making such a blunder so maybe I misremember after all. – Konrad Rudolph Jun 06 '16 at 12:25
  • 2
    One more thing to fix: the phrase "i.e. not the virtual one" misleadingly indicates that calls from a constructor are not virtual, but are resolved statically. But the virtual call mechanism works exactly the same in a constructor as elsewhere. In particular, if there is a call to a base class method that in turn calls a virtual method `v`, and `v` has been overridden in this class, then it's the overrider of `v` that's called. – Cheers and hth. - Alf Jun 06 '16 at 13:16
13

Well you have already linked to the FAQ's discussion, but that’s mainly problem-oriented, not going into the rationales, the why.

In short, it’s for type safety.

This is one of the few cases where C++ beats Java and C# on type safety. ;-)

When you create a class A, in C++ you can let each A constructor initialize the new instance so that all common assumptions about its state, called the class invariant, hold. For example, part of a class invariant can be that a pointer member points to some dynamically allocated memory. When each publicly available method preserves the class invariant, then it’s guaranteed to hold also on entry to each method, which greatly simplifies things – at least for a well-chosen class invariant!

No further checking is then necessary in each method.

In contrast, using two-phase initialization such as in Microsoft's MFC and ATL libraries you can never be quite sure whether everything has been properly initialized when a method (non-static member function) is called. This is very similar to Java and C#, except that in those languages the lack of class invariant guarantees comes from these languages merely enabling but not actively supporting the concept of a class invariant. In short, Java and C# virtual methods called from a base class constructor can be called down on a derived instance that has not yet been initialized, where the (derived) class invariant has not yet been established!

So, this C++ language support for class invariants is really great, helping do away with a lot of checking and a lot of frustrating perplexing bugs.

However, it makes a bit difficult to do derived class specific initialization in a base class constructor, e.g. doing general things in a topmost GUI Widget class’ constructor.

The FAQ item “Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?” goes a little into that.

For a more full treatment of the most common case, see also my blog article “How to avoid post-construction by using Parts Factories”.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • "However, it makes a bit difficult to do derived class specific initialization in a base class constructor, e.g. doing general things in a topmost GUI Widget class’ constructor." What a qutote. It is the first time that I see a disadvantage in C++ approach. Many thanks. – user1284631 Sep 22 '14 at 21:33
  • "difficult to do derived class specific initialization in a base class constructor" - nonsense. base class can NEVER do anything in derived classes as derived simply do not exist when base class constructor is executing... – Califf Jun 06 '16 at 05:53
  • @Califf: You could have read on to the next paragraph, which links to the FAQ item about it. Then you could have learned something, including that this link was now stale, leading to the original no-longer existing FAQ location, and where the FAQ has moved. Even though you were not aware of that and it was not your intention, thanks for making /me/ aware of this issue here. I thought all such links had been updated. Anyway, it's always/often a good idea to check the FAQ. Please do so now, and do feel free to ask more if there are then any remaining questions. – Cheers and hth. - Alf Jun 06 '16 at 06:54
  • @Califf: Please do not take the FAQ item as an authority argument, however. It's just technical information. As an authority argument it would be rather circular since it was I who convinced Marshall Cline to include it in the original FAQ. It's Marshall Cline's wording though. Including the acronym DBDI, which never caught on. – Cheers and hth. - Alf Jun 06 '16 at 06:57
  • @Califf: Oh, you downvoted based on your ignorance. Would you mind removing that downvote now? – Cheers and hth. - Alf Jun 06 '16 at 07:24
7

Regardless of how it's implemented, it's a difference in what the language definition says should happen. Java allows you to call functions on a derived object that hasn't been fully initialized (it has been zero-initialized, but its constructor has not run). C++ doesn't allow that; until the derived class's constructor has run, there is no derived class.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
2

Hopefully this will help:

When your line new Derived() executes, the first thing that happens is the memory allocation. The program will allocate a chunk of memory big enough to hold both the members of Base and Derrived. At this point, there is no object. It's just uninitialized memory.

When Base's constructor has completed, the memory will contain an object of type Base, and the class invariant for Base should hold. There is still no Derived object in that memory.

During the construction of base, the Base object is in a partially-constructed state, but the language rules trust you enough to let you call your own member functions on a partially-constructed object. The Derived object isn't partially constructed. It doesn't exist.

Your call to the virtual function ends up calling the base class's version because at that point in time, Base is the most derived type of the object. If it were to call Derived::virt, it would be invoking a member function of Derived with a this-pointer that is not of type Derrived, breaking type safety.

Logically, a class is something that gets constructed, has functions called on it, and then gets destroyed. You can't call member functions on an object that hasn't been constructed, and you can't call member functions on an object after it's been destroyed. This is fairly fundamental to OOP, the C++ language rules are just helping you avoid doing things that break this model.

je4d
  • 7,628
  • 32
  • 46
0

In Java, method invocation is based on object type, which is why it is behaving like that (I don't know much about c++).

Here your object is of type Derived, so jvm invokes method on Derived object.

If understand Virtual concept clearly, equivalent in java is abstract, your code right now is not really virtual code in java terms.

Happy to update my answer if something wrong.

kosa
  • 65,990
  • 13
  • 130
  • 167
0

Actually I want to know what's the insight behind this design decision

It may be that in Java, every type derives from Object, every Object is some kind of leaf type, and there's a single JVM in which all objects are constructed.

In C++, many types aren't virtual at all. Furthermore in C++, the base class and the subclass can be compiled to machine code separately: so the base class does what it does without whether it's a superclass of something else.

ChrisW
  • 54,973
  • 13
  • 116
  • 224
0

Constructors are not polymorphic in case of both C++ and Java languages, whereas a method could be polymorphic in both languages. This means, when a polymorphic method appears inside a constructor, the designers would be left with two choices.

  • Either strictly conform to the semantics on non-polymorphic constructor and thus consider any polymorphic method invoked within a constructor as non-polymorphic. This is how C++ does§.
  • Or, compromise the strict semantics of non-polymorphic constructor and adhere to the strict semantics of a polymorphic method. Thus polymorphic methods from constructors are always polymorphic. This is how Java does.

Since none of the strategies offers or compromises any real benefits compared to other and yet Java way of doing it reduces lots of overhead (no need to differentiate polymorphism based on the context of constructors), and since Java was designed after C++, I would presume, the designer of Java opted for the 2nd option seeing the benefit of less implementation overhead.

Added on 21-Dec-2016


§Lest the statement “method invoked within a constructor as non-polymorphic...This is how C++ does” might be confusing without careful scrutiny of the context, I’m adding a formalization to precisely qualify what I meant.

If class C has a direct definition of some virtual function F and its ctor has an invocation to F, then any (indirect) invocation of C’s ctor on an instance of child class T will not influence the choice of F; and in fact, C::F will always be invoked from C’s ctor. In this sense, invocation of virtual F is less-polymorphic (compared to say, Java which will choose F based on T)
Further, it is important to note that, if C inherits definition of F from some parent P and has not overriden F, then C’s ctor will invoke P::F and even this, IMHO, can be determined statically.
KGhatak
  • 6,995
  • 1
  • 27
  • 24
  • A "method" (virtual function) invoked from inside a C++ constructor is polymorphic. – curiousguy Nov 30 '16 at 16:38
  • @Curiousguy - Say, class _Base_ has functions ‘_**V1**_’, and ‘_**Disp**_’ as: `virtual void v1(){disp();}` and `virtual void disp(){cout <<"Base";}`. The functions in _Derived_ class which inherits _Base_ are as: `virtual void disp(){cout <<"Derived";}` and `void test(){v1();}`. Now for a statement like `Derived d;d.test();` – conceptually we’ve two possibilities of output –(A) “Base” and (B)”Derived” get printed. Let me ask you, under what condition you’ll assert the behavior of ‘_**Disp**_’ is polymorphic – for (A), for (B), or both – KGhatak Dec 01 '16 at 07:45
  • The behavior is polymorphic when dispatching is done based on the real time; when dispatching is done based on the declared type, it is static dispatch. – curiousguy Dec 05 '16 at 14:22
  • The polymorphic behavior of virtual function calls means the function called is the final overrider of the real type of the object, it means the type of object constructed. Inside a constructor body, the real type of `*this` is always the same the declared type of `*this`, the same as the constructor: inside `Base::Base()`, the real type of `*this` is `Base`. – curiousguy Dec 05 '16 at 14:32
  • @curiousguy - First, if you put yourself in a language designer's shoe, you'll realize that when calling a function F (embellished with key word virtual in C++) in relation to an object of real-time type T from a constructor of a class C , it is very trivial to enforce polymorphic F "as long as" T and C are NOT related by inheritance (excluding corner cases if any). I guess, this is the case you had in mind when you've posted your 1st comment. However, my statement (1st bullet in the answer) was for the case when C and T are related. Hope this clarify the confusion. – KGhatak Dec 05 '16 at 15:12
  • @curiousguy - Btw, wud u pls elaborate your 3rd comment - sorry didn't get it. – KGhatak Dec 05 '16 at 15:13
  • Inside a ctor body `C::C()`, the compile type of `*this` is `C` by definition and the real type is also `C`, so any polymorphic call on `this` will call the function defined in class `C`. – curiousguy Dec 07 '16 at 00:44
  • @curiousguy - The catch is that, the grammar (of realtime type inside a constructor) you've mentioned above is the outcome of some architectural decision pertaining to the concern -**how to treat an intended polymorphic function F on type T when invoked from a constructor of type C given that C and T are related by inheritance**. The aforesaid grammar may be the best for certain language but needn’t to be the only possible decision. In fact, Java has taken a different decision for the same architectural concern. Before I proceed further, pls let me know if you agree and otherwise why not! – KGhatak Dec 07 '16 at 08:24
  • All polymorphic function calls are treated the same: the final overrider is invoked, always. It doesn't matter that the call is from a ctor, from a function called from inside a ctor, etc. It's a general rule. It doesn't matter what the relation between C and T is, during construction, the type of the object being constructed is the class of the constructor. – curiousguy Dec 07 '16 at 15:26
  • @curiousguy - The above statement is true for all languages or any specific language? Can u pls specify! – KGhatak Dec 09 '16 at 14:25
  • Yes, in both Java and C++, the final overrider is called. But in C++, the type of the object being constructed is always the same as the running ctor. In Java, the real type is the one created by `new`, even during construction. – curiousguy Dec 10 '16 at 17:34
  • @curiousguy - So we see that the 'final overrider' would be called in both the languages, however, the rule to determine one such overrider varies. Now, if I rephrase your comment using the formalization (I mentioned earlier) using types C and T, and virtual function F, then we see that the final overrider in case of C++ would be `C::F` whereas for Java would be `T::F`. Do you agree! – KGhatak Dec 11 '16 at 20:41
  • The final overrider is the one of the **dynamic** type of the object. – curiousguy Dec 12 '16 at 18:22
  • @curiousguy - Do you agree that the final overrider (for our context) would be `C::F` in case of C++ and `T::F` in case of Java? – KGhatak Dec 12 '16 at 18:47
  • If C is the constructor running and T the class that is created with new, then yes. – curiousguy Dec 12 '16 at 19:17
  • @curiousguy - You got the context right..( it is also formalized, in bold letters, in my comment on 7th Dec) – KGhatak Dec 13 '16 at 06:53
  • You see, the difference in _final-overrider_ is because the `this` variable inside C’s ctor is evaluated as `C` for C++; but for Java, it is `T`. Further, for **such context**, since the runtime-type (`C`in this case) of `this` always matches its compile-time type for C++, the _final-overrider_ can also be decided at compile time. This is not the case for Java. So, pertaining to _such context_, we can now infer that an invocation to virtual `F` is less polymorphic in C++ than in Java, as unlike Java, the _final-overrider_ can be decided statically in C++. This was precisely my point in my post – KGhatak Dec 13 '16 at 07:15
  • @curiousguy – This post received a down vote almost at the same time when you’ve challenged the post via your comment. So presumably, the down-vote seems to be from you. If this is the case, then it would be nice of you to respond to the further clarifications from the author of the post, and to conclude/justify the down-vote. Thanks in advance for your cooperation. – KGhatak Feb 02 '17 at 17:07
  • @curiousguy, The definition is: "polymorphism refers to a programming language's ability to process objects differently depending on their data type or class". In C++ the constructor does not obey the definition, so it is absolutely right to say that any polymorphic method invoked within a constructor is processed as non-polymorphic. As KGhatak stated. – JRr Jun 08 '17 at 08:32
  • @joro Just no. In a constructor, the type is known, by definition. Just as the value of 0 is known to be 0. An integer variable can have different values and 0 is still 0. A language with arithmetic capabilities can deal with different integer values but 0 is still a language construct that does not have variability, you can't even discuss the variability of 0 as it would be a meaningless discussion. The constructor `T::T()` constructs an object of type `T`, **by definition**. – curiousguy Jun 10 '17 at 21:51
  • "_"polymorphism refers to a programming language's ability (...)_" **A constructor is a programming language construct, not a programming language**, so the definition does not apply (to constructor, or constants, or code blocks, or for loops, or any other programming language construct that does not deal directly with polymorphism). It doesn't make sense to ask if constructors support polymorphism, period. "_any polymorphic method invoked within a constructor is processed as non-polymorphic_" Define "processed as polymorphic" please. – curiousguy Jun 10 '17 at 21:53
  • @curiousguy 1. I won't define what is "processed/invoked as polymorphic" , because KGhatak did that. 2. No one has stated that a constructor is NOT a programming language construct. I have stated that the definition of polymorphism is not obeyed by constructors. 3. General methods are also programming language construct also but they obey the definition and provide ability to process objects differently, in contrast of constructors. 4. The definition of polymorphism does not concerns implementation details as whether constructor T::T() produces T objects or Apples. – JRr Jul 10 '17 at 15:13