16

I have written - what seemed to be - exactly the same example of inheritance both in Java and C++. I am really amazed to see the different outputs of these programs. Let me share both code snippets and the corresponding outputs.


C++ Code:

class A
{
public:
    A() {}
    void sleep() {
        cout << "A.Sleep" << endl;
        eat();
    }
    void eat() {cout << "A.Eat" << endl;}
};

class B: public A
{
public:
    B() {}
    void sleep() {
        A::sleep();
        cout << "B.Sleep " <<endl;
        this->eat();
    }
    void eat() {
        cout << "B.Eat" << endl;
        run();
    }
    void run() {
        A::sleep();
        cout << "B.run" << endl;
    }
};

int main()
{
    B *b = new B();
    b->sleep();
}

Output:

A.Sleep
A.Eat
B.Sleep
B.Eat
A.Sleep
A.Eat
B.run

executed successfully...

Java Code:

class A
{
    A() {}
    void sleep() {
        System.out.println("A.Sleep");
        this.eat();
    }
    void eat() { System.out.println("A.Eat");}
};

class B extends A
{
    B() {}
    @Override
    void sleep() {
        super.sleep();
        System.out.println("B.Sleep");
        this.eat();
    }
    @Override
    void eat() {
        System.out.println("B.Eat");
        run();
    }
    void run() {
        super.sleep();
        System.out.println("B.Run");
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.sleep();
    }
}

Output:

A.Sleep
B.Eat
A.Sleep
B.Eat
A.Sleep
......
......
......
(Exception in thread "main" java.lang.StackOverflowError)

I don't know why these two examples of inheritance behave differently. Shouldn't it work similarly?

What is the explanation for this scenario?

Neuron
  • 5,141
  • 5
  • 38
  • 59
RajibTheKing
  • 1,234
  • 1
  • 15
  • 35
  • 18
    In Java all methods are virtual (i.e. subject to overriding). In C++ they are not. If you give a subclass a method with the same name as a non-virtual method in the base class, they're just two similarly named methods. – khelwood Apr 12 '18 at 11:42
  • 18
    I would change the title/question to something like "**What** is the difference between C++ and Java inheritance in this case?", because the **why?** is simply because c++ and java are two different languages – 463035818_is_not_an_ai Apr 12 '18 at 11:45
  • See https://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c – khelwood Apr 12 '18 at 11:45
  • 3
    Why wouldn't it be different? Making parallels between the two unrelated languages is often counter-productive. – Ron Apr 12 '18 at 11:45
  • 2
    Because methods in Java are [dynamic bounded](https://en.wikipedia.org/wiki/Dynamic_binding) by default. – Jacob G. Apr 12 '18 at 11:46
  • It's called method overriding here is a useful link they explain it https://www.geeksforgeeks.org/overriding-in-java/ – urag Apr 12 '18 at 11:46
  • 2
    Try `void sleep() override {...` in the C++ code and see what the compiler thinks about it. – molbdnilo Apr 12 '18 at 11:48
  • 4
    "I have written exactly same example of inheritance both in Java and C++" - no you haven't. You tried to get as close to the same syntax as you could (and didn't go far enough with the override keyword). – Mat Apr 12 '18 at 11:49
  • 1
    @Mat I am sure he is aware that it isn't *exactly* the same, as he himself knows the output is different. Those words where chosen to make the paradox obvious he stumbled across. – Neuron Apr 12 '18 at 12:05
  • @khelwood while it would seem that a duplicate likely exists for this question, that link does not contain an appropriate duplicate target. – Krupip Apr 12 '18 at 16:12
  • Why *shouldn't* they behave differently? They are two different languages. – Masked Man Apr 12 '18 at 18:04
  • One explanation is still vague to me....... "Why this java program ends up with ...StackOverflowError ??" – RajibTheKing Apr 12 '18 at 20:06
  • Because java program ends up falling into an infinite recursion, `sleep()->eat()->sleep()->eat()`. Eventually running outof stack memory. More: https://stackoverflow.com/questions/214741/what-is-a-stackoverflowerror – MD Ruhul Amin Apr 12 '18 at 23:49

2 Answers2

25

In your C++ example you are hiding the base methods, but you don't override them. So they are actually different methods which just happen to have the same name. If you are calling

A* a = new B();
a->sleep();

it will actually print "A.Sleep". If you want to override a method, you need to declare it virtual in the Base class (automatically making it virtual in all sub classes too). You can read more about function hiding vs overriding in C++ in this post.

In your Java example you actually override the methods, so they are the same method. One taking the place of the old. You can think of it this way: all Java functions are secretly marked as virtual, meaning they can be overridden. If you want a method to not be overridable in Java, you must declare it final.

Neuron
  • 5,141
  • 5
  • 38
  • 59
6

Note: be careful, every language as its own way of thinking. There is a lot of ways to interpret/implement OO. Even if C++ and Java looks similar, they are far from similar.

In both languages, the compiler verifies at compile-time if you can call a method, by examining the class (and the one inherited from the current one, etc) for a method of the right signature and visibility. What makes things different is the way the call is really emitted.

C++:

In the case of non-virtual methods the method called is fully determined at compile-time. This is why even if the object is of class B, when it is executing A::sleep the call to eat is resolved as a call to A::eat (eat is not virtual then compiler calls A::eat because you are in level A). In B::sleep() the call to this->eat() is resolved as a call to B.eat() because at that place this is of type B. You can't go down to the inheritance hierarchy (call to eat in class A will never call an eat method in a class below).

Be aware that things are different in the case of virtual methods (it is more similar to the Java case while being different).

Java:

In Java, the method called is determined at run-time, and is the one that is the most related to the object instance. So when in A.sleep the call to eat will be a call related to the type of the current object, that means of the type B (because the current object is of type B) then B.eat will be called.

You then have a stack overflow because, as you are playing with an object of type B a call to B.sleep() will call A.sleep(), which will call B.eat(), which in turn will call B.run() which will call A.sleep(), etc in a never ending loop.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69