简体   繁体   中英

Why does inheritance behave differently in Java and C++ with superclasses calling (or not) subclasses' methods?

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?

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM