[英]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.