为什么在Java和C++中,继承在超类调用(或不是)子类的方法时表现不同?

Raj*_*ing 16 c++ java inheritance

我写过 - 似乎是 - 在Java和C++中完全相同的继承示例.看到这些计划的不同产出,我感到非常惊讶.让我分享代码片段和相应的输出.


C++代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

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

executed successfully...
Run Code Online (Sandbox Code Playgroud)

Java代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

A.Sleep
B.Eat
A.Sleep
B.Eat
A.Sleep
......
......
......
(Exception in thread "main" java.lang.StackOverflowError)
Run Code Online (Sandbox Code Playgroud)

我不知道为什么这两个继承的例子表现不同.它不应该同样有效吗?我很好奇...... 这个场景的解释是什么?

Neu*_*ron 23

在您的C++示例中,您隐藏了基本方法,但您不会覆盖它们.所以它们实际上是恰好具有相同名称的不同方法.如果你打电话

A* a = new B();
a->sleep();
Run Code Online (Sandbox Code Playgroud)

它实际上会打印出来"A.Sleep".如果要覆盖方法,则需要virtual在Base类中声明它(在所有子类中自动使其成为虚拟).在本文中,您可以阅读有关C++中函数隐藏与覆盖的更多信息.

在Java示例中,您实际上覆盖了方法,因此它们是相同的方法.一个取代旧的.您可以这样想:所有Java函数都被秘密标记为virtual,这意味着它们可以被覆盖.如果希望方法在Java中不可覆盖,则必须声明它final.


Jea*_*nès 5

注意:小心,每种语言都有自己的思维方式。有很多方法可以解释/实现 OO。即使 C++ 和 Java 看起来相似,它们也远非相似。

在这两种语言中,编译器会在编译时验证您是否可以调用方法,方法是检查类(以及从当前类继承的类等)以获取具有正确签名和可见性的方法。使事情不同的是调用的真正发出方式。

C++

在非虚拟方法的情况下,调用的方法在编译时完全确定。这就是为什么即使对象属于 class B,当它执行A::sleep调用时,对 的调用也eat被解析为对A::eat(eat不是虚拟的,然后编译器调用,A::eat因为您在 level 中A)。在B::sleep()调用中this->eat()被解析为调用,B.eat()因为在那个地方this是类型B。您不能深入到继承层次结构(eat在类中A调用永远不会调用eat下面类中的方法)。

请注意,虚拟方法的情况有所不同(它更类似于 Java 的情况,但有所不同)。

爪哇

在 Java 中,调用的方法是在运行时确定的,并且是与对象实例最相关的方法。所以当A.sleep调用 toeat将是一个与当前对象的类型相关的调用时,该类型的意思B(因为当前对象是 type B)然后B.eat将被调用。

然后你有一个堆栈溢出,因为当你在玩一个类型的对象时,会在一个永无止境的循环中B调用B.sleep()will call A.sleep(),这将调用B.eat(),而后者又会调用B.run()which will callA.sleep()等。