簡體   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