[英]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()
的调用,因为在那个地方this
是B
类型。 你不能再往继承层次(调用eat
类A
永远不会叫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.