繁体   English   中英

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

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

我已经写了 - 似乎是 - 在 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();
}

输出:

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

executed successfully...

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();
    }
}

输出:

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

我不知道为什么这两个继承示例的行为不同。 它不应该类似地工作吗?

这个场景的解释是什么?

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

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

它实际上会打印"A.Sleep" 如果你想覆盖一个方法,你需要在 Base 类中将它声明为virtual (在所有子类中也自动使其成为 virtual )。 您可以在这篇文章中阅读有关 C++ 中函数隐藏与覆盖的更多信息

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

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

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

C++

在非虚拟方法的情况下,调用的方法在编译时完全确定。 这就是为什么即使对象属于B类,当它执行A::sleep ,对eat的调用也被解析为对A::eat的调用( eat不是虚拟的,然后编译器调用A::eat因为你在A级)。 B::sleep() ,对this->eat()的调用被解析为对B.eat()的调用,因为在那个地方thisB类型。 你不能再往继承层次(调用eatA永远不会叫eat在下一个类的方法)。

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

爪哇

在 Java 中,调用的方法是在运行时确定的,并且是与对象实例最相关的方法。 因此,当在A.sleep中调用eat将是与当前对象的类型相关的调用,即B类型的调用(因为当前对象是B类型)然后B.eat将被调用。

然后,您会遇到堆栈溢出,因为当您使用B类型的对象时,对B.sleep()调用将调用A.sleep() ,后者将调用B.eat() ,后者又将调用B.run()它将在永无止境的循环中调用A.sleep()等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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