我写过 - 似乎是 - 在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)
我不知道为什么这两个继承的例子表现不同。难道它的工作方式不同吗?我很想知道...... 这种情况的解释是什么?
答案 0 :(得分:23)
在您的C ++示例中,您是hiding基本方法,但您不能覆盖它们。所以它们实际上是恰好具有相同名称的不同方法。如果你打电话
A* a = new B();
a->sleep();
它实际上会打印"A.Sleep"
。如果要覆盖方法,则需要在Base类中声明它virtual
(在所有子类中自动使其成为虚拟)。您可以在this post中阅读有关函数隐藏与覆盖C ++的更多信息。
在Java示例中,您实际上覆盖了方法,因此它们是相同的方法。一个取代旧的。您可以这样考虑:所有Java函数都被秘密标记为virtual
,这意味着它们可以被覆盖。如果希望方法在Java中不可覆盖,则必须将其声明为final
。
答案 1 :(得分:4)
注意:要小心,每种语言都是思考的方式。有很多方法可以解释/实现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()
等。