2012-08-02 64 views
4

我從來沒有使用多重繼承,但最近閱讀它時,我開始考慮如何在代碼中實際使用它。當我使用多態通常我通常通過創建聲明爲基類指針新衍生的實例使用諸如你如何實現多繼承的多態行爲?

BaseClass* pObject = new DerivedClass(); 

,讓我在派生類中調用虛函數時,得到正確的多態行爲。通過這種方式,我可以擁有不同多態類型的集合,通過它們的虛擬功能來管理自己的行爲。

當使用多重繼承考慮,我在想同樣的做法,但我會怎麼做,如果我有以下層次

class A { 
    virtual void foo() = 0; 
}; 
class B : public A { 
    virtual void foo() { 
     // implementation 
    } 
}; 

class C { 
    virtual void foo2() = 0; 
}; 
class D : public C { 
    virtual void foo2() { 
     // implementation 
    } 
}; 

class E : public C, public B { 
    virtual void foo() { 
     // implementation 
    } 
    virtual void foo2() { 
     // implementation 
    } 
}; 

這個層次,我可以創建E類作爲一個新的實例

A* myObject = new E(); 

C* myObject = new E(); 

E* myObject = new E(); 

但如果我聲明它爲A*那麼我將失去C和D繼承層次的多態性。同樣,如果我將其聲明爲C*,那麼我將失去A和B類多態性。如果我聲明它爲E*那麼我不能以我通常所做的方式獲得多態行爲,因爲不通過基類指針訪問對象。

所以我的問題是什麼解決這個? C++是否提供了一種可以解決這些問題的機制,或者必須將指針類型在基類之間來回轉換?當然這很麻煩,因爲我不能直接做以下操作

A* myA = new E(); 
C* myC = dynamic_cast<C*>(myA); 

因爲cast會返回一個NULL指針。

+0

爲什麼要將它轉換爲'A *'然後賦值給'C *'? – 2012-08-02 07:14:11

+3

「我實際上可以如何使用它」聽起來好像你只是想因爲可能而試圖使用它。如果有充分的理由,請使用它。 – Alex 2012-08-02 07:14:35

+0

@JoachimPileborg要訪問C類端多態性 – mathematician1975 2012-08-02 07:18:25

回答

7

通過多繼承,您可以使用單個對象查看多種不同方式。舉個例子:

class door { 
    virtual void open(); 
    virtual void close(); 
}; 

class wood { 
    virtual void burn(); 
    virtual void warp(); 
}; 

class wooden_door : public wood, public door { 
    void open() { /* ... */ } 
    void close() { /* ... */ } 
    void burn() { /* ... */ } 
    void warp() { /* ... */ } 
}; 

現在,如果我們創建一個wooden_door對象,我們可以把它傳遞給需要用(引用或指針),一個門的對象,或希望的功能工作的功能使用(再次指向或參照)一個wood對象。

這是千真萬確的多重繼承將突然放棄與door小號任何新的工作能力與wood(反之亦然)工作的功能 - 但我們並不真正指望。我們所期望的是能夠把我們的木門當作門可以打開和關閉,或者作爲一塊可以燃燒或翹曲的木頭 - 這正是我們所得到的。

1

這應該工作

A* myA = new E(); 
E* myC = dynamic_cast<E*>(myA); 
myC->Foo2(); 
0

C不能轉換爲A,因爲它不是一個A;它只能投下DE

使用A*可以使一個E*並通過你總是可以明確地說這樣的話C::foo()但肯定的,也沒有辦法爲A隱式調用功能C可能有替代或可能不會。

在奇怪的情況就是這樣,模板往往是一個很好的解決方案,因爲他們可以允許類充當如果他們有,即使他們沒有共同繼承。例如,你可以編寫一個可以在其上調用foo2()的任何模板。

3

在這種情況下,類AC是接口,並且E實現了兩個接口 。 (通常,在這種情況下,您不會有中間類別CD)。有幾種處理此問題的方法。

最常見的可能是定義一個新的接口,這是一筆AC

class AandC : public A, public C {}; 

,並有E源於此。然後,您通常會通過 AandC*來管理E,並將其無差別地傳遞給採用A*C*的函數。在同一對象中需要兩個接口的函數將使用AandC*處理 。

如果接口AC有某種聯繫,說C提供一些A(但不是全部)可能要 支持,那麼它可能是有意義的A有一個getB()函數返回 額外的設施, C*(或者一個空指針,如果對象不支持 的接口,則爲C)。

最後,如果你有混入多個接口,最乾淨的 解決方案是維護兩個獨立的層次,一爲 接口,另一個與實現部分:

// Interface... 
class AandC : public virtual A, public virtual C {}; 

class B : public virtual A 
{ 
    // implement A... 
}; 

class D : public virtual C 
{ 
    // implement C... 
}; 

class E : public AandC, private B, private D 
{ 
    // may not need any additional implementation! 
}; 

(I」 m試圖說從設計的角度來看,接口的繼承應該始終是虛擬的,以便在將來可以使用這種類型的事情,即使現在不需要它,但實際上,它似乎是公平的很少有人不能預測這種用途n前進。)

如果你想要了解更多關於這類事情的信息,你可能需要 閱讀Barton和Nackman。這本書現在相當陳舊了(它描述了 pre C++ 98),但大部分信息仍然有效。