2011-07-20 186 views
21

我沒有C++的經驗,我來自Java背景。最近,我在一次採訪中被問及爲什麼Java不允許多重繼承,答案很簡單。不過,我仍然對C++如何處理它感到好奇,因爲它允許你從多個類繼承。多個超類的多繼承,C++和相同方法簽名

具體來說,就是說有一個叫MechanicalEngineer的類,另一個叫ElectricalEngineer。兩者都有一個名爲buildRobot()的方法。

如果我們把第三類RoboticsEngineer會發生什麼,來自兩個inherets並不會覆蓋方法,你只要致電:

(some instance of RoboticsEngineer).buildRobot() 

會拋出異常,或者從一個方法超類將被使用?如果是這樣,編譯器如何知道使用哪個類?

+4

如果一個類有一個方法'buildRobot'並且一個接口有一個'buildRobot'方法並且你定義了一個也實現了接口的子類,那麼Java會發生什麼? – toto2

+0

@toto顯然,只要返回類型相同就可以,請參閱此[線程](http://stackoverflow.com/questions/2801878/implemeting-2-interfaces-in-a-class-with-same-方法 - 這界面法-是-OV)。 – toto2

回答

20

編譯器會將這種情況(即試圖調用(some instance of RoboticsEngineer).buildRobot())標記爲錯誤。

這是因爲派生類對象已經得到了雙方的基本對象的副本(A MechanicalEngineer實例和ElectricalEngineer實例)的自身內部和單獨的方法簽名是不夠的,告訴使用哪一個。

如果您在RoboticsEngineer覆蓋buildRobot,你就可以明確地說,其繼承的方法通過在前面的類名來使用,例如:

void RoboticsEngineer::buildRobot() { 
    ElectricalEngineer::buildRobot() 
} 

通過同一枚硬幣,你實際上可以「力」在這種情況下

(some instance of RoboticsEngineer).ElectricalEngineer::buildRobot(); 

ElectricalEngineer實施方法將被調用,不會產生歧義:編譯器通過與類名前綴它使用一個版本或其他的buildRobot

當你有一個Engineer基類都MechanicalEngineerElectricalEngineer和您指定的繼承是在這兩種情況下virtual一種特殊情況給出。當使用virtual時,派生對象不包含Engineer的兩個實例,但編譯器確保只有一個實例。這應該是這樣的:

class Engineer { 
     void buildRobot(); 
}; 

class MechanicalEngineer: public virtual Engineer { 

}; 

class ElectricalEngineer: public virtual Engineer { 

}; 

在這種情況下,

(some instance of RoboticsEngineer).buildRobot(); 

將可以解決,而模糊。如果buildRobot被聲明爲virtual並且在兩個派生類之一中被重寫,情況也是如此。無論如何,如果兩個派生類(ElectricalEngineer和MechanicalEngineer)都會覆蓋buildRobot,則會再次產生歧義,編譯器會將調用(some instance of RoboticsEngineer).buildRobot();時的嘗試標記爲錯誤。

+0

可以說虛擬是相同的關鍵詞「實施」在Java? –

+0

@Yochai Timmer:你能更精確嗎?兩者都應該是虛擬的?繼承或方法?我所做的任何陳述都得到了一個編譯器測試的支持...... @Kit Ho:我不太瞭解Java,但是因爲虛擬繼承對於多繼承是有意義的,而且Java沒有這個,所以我會懷疑它... – sergio

+0

@sergio:在你編輯你之前說過,你只需要使兩個方法都是虛擬的。你現在有什麼工作。雖然OP問如果兩個基類都實現了這個方法會發生什麼(問題不在於鑽石繼承) –

5

它不處理它。它含糊不清。 error C2385: ambiguous access of 'functionName'

編譯器很聰明,知道它不應該猜出你的意思。
對於要編譯的程序,您需要告訴編譯器:
答:您知道這是一個有問題的問題。B,告訴它你究竟是什麼意思。

要做到這一點,你需要明確地告訴你問哪個方法編譯:

RoboticsEngineer myRobot; 
myRobot.ElectricalEngineer::buildRobot(); 
0

編譯器會抱怨這種situtation的。

在這種情況下,C++建議使用「純虛擬」方法buildRobot()函數創建接口。 MechanicalEngineer和EletricalEnginner將繼承接口並覆蓋buildRoboot()函數。

當您創建RoboticsEnginner對象並調用buildRobot()函數時,將調用該接口的函數。

+0

是否reli C++可以從兩個以上的類繼承? –

0
struct MecEngineer { 

    void buildRobot() { /* .... */ } 

}; 

struct EleEngineer { 

    void buildRobot() { /* .... */ } 

}; 

struct RoboticsEngineer : MecEngineer, EleEngineer { 

}; 

現在,當你這樣做,

​​

這樣的呼叫不能得到解決,是模糊的,因爲無論是子對象具有相同的簽名和編譯器的成員函數不知道哪一個呼叫。在這種情況下,您需要使用::運營商或使用static_cast明確提及。

static_cast<MecEngineer*> (robEngObject) -> buildRobot() ; 
+0

演示:http://ideone.com/EgD7B – Mahesh

+0

實際上沒有合乎邏輯的理由來使用範圍操作符是足夠的,更短的,並且通常在實踐中更可接受。 –

0

沒有關於多個類的繼承允許這一點。 Java產生與接口相同的問題。

public interface A { 
    public void doStuff(); 
} 
public interface B { 
    public void doStuff(); 
} 
public class C implements A, B {} 

簡單的答案是編譯器會在其上引發錯誤。

+0

在這種情況下,編譯器爲什麼會拋出錯誤/會拋出什麼錯誤?記住Java中的接口就像純虛函數一樣。因此C需要實現這個函數,調用它將不會造成歧義。 – Pratham