2016-04-04 62 views
1

不知道該如何解釋好,所以我將只提供一個代碼示例,演示我的問題:調用指定的方法

class Base { 
public: 
    Base() = default; 
    ~Base() = default; 

    virtual void stuff(std::shared_ptr<Base> b) = 0; 
}; 

class DerivedA : public Base { 
public: 
    DerivedA() = default; 
    ~DerivedA() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
}; 

class DerivedB : public Base { 
public: 
    DerivedB() = default; 
    ~DerivedB() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
    void stuff(std::shared_ptr<DerivedA> b) { 
    std::cout << "stuff Derived" 
       << "\n"; 
    } 
}; 

int main(int argc, char *argv[]) { 
    std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    return 0; 
} 

輸出將是:

stuff Base 
stuff Base 

現在,我想不可能調用派生方法,因爲它不存在於基類中。

我的問題是:有沒有辦法使用基類來調用stuff(std::shared_ptr<DerivedA> b)

[編輯]

我已經想到了訪問者模式(應該說,這和更具體)。

我的類代表實體,我必須檢查它們之間的衝突。然而,一個& B之間的碰撞將有較B C.

之間&

我同意,這將工作不同的效果,但它意味着我將要定義噸的方法。

有沒有更好的方法來做到這一點?

在此先感謝。

+0

這看起來像訪客模式的工作! https://en.wikipedia.org/wiki/Visitor_pattern – Louen

+3

看起來像是在方法(或任何函數)重載和(虛擬)方法覆蓋之間混淆了。 – hyde

+0

我編輯了我的問題以回答@Louen – Luc

回答

1

你所尋找的是通常被稱爲多重調度,或多重方法。在C++中沒有直接的語言支持,但您可以自己明確地實現它。基本上,你有分派到另一個虛擬函數的具體對象的一個​​虛函數:

struct DerivedA; 
struct DerivedB; 

struct Base { 
    virtual ~Base() = default; 
    virtual void stuff(shared_ptr<Base>) = 0; 

    virtual void dispatch_stuff(Base&) = 0; 
    virtual void dispatch_stuff(DerivedA& p) { return dispatch_stuff(static_cast<Base&>(p)); } 
    virtual void dispatch_stuff(DerivedB& p) { return dispatch_stuff(static_cast<Base&>(p)); } 

struct DerivedA : Base { 
    void stuff(shared_ptr<Base> rhs) override { 
     rhs->dispatch_stuff(*this); 
    } 

    void dispatch_stuff(Base&) { /* default */ } 
    void dispatch_stuff(DerivedA&) { /* specific A-A stuff */ } 
}; 

這樣:

b1->stuff(b2); // calls b2->dispatch_stuff(DerivedA&) 
b2->stuff(b1); // calls b1->dispatch_stuff(DerivedB&) 
1

我的問題是:有沒有辦法使用基類調用 stuff(std::shared_ptr<DerivedA> b)

不,因爲Base類接口沒有實現你想調用的方法。儘管Base類指針指向的是一個DerivedB對象,並且通過poilformism可以解決指針指向的對象類型(即DerivedB)的方法,但您只能調用Base類中定義的方法。因此,您不能使用Base指針指向stuff(std::shared_ptr<DerivedA> b)指針指向DerivedB對象。

例如:

std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 
    std::shared_ptr<DerivedA> a1(new DerivedA()); 
    std::shared_ptr<DerivedB> bb1(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    b2->stuff(a1); // b2 is the only class that implement stuff(std::shared_ptr<DerivedA> b) 
    bb1->stuff(a1) 

輸出:

stuff Base 
stuff Base 
stuff Base 
stuff Derived 
stuff Base 
1

你正在試圖解決的問題稱爲double dispatch問題,這意味着你正試圖根據兩個對象的具體類型調用一個行爲。在谷歌或這裏查找這個術語可能會產生一些有趣的結果。

首先,這種或那種方式,你將不得不編寫許多函數,因爲如果你有N個不同的類型,N個可能的配對就是N 。 (如果訂單無關緊要,N N/2,在碰撞情況下可能是這種情況)。

visitor pattern是雙調度問題的標準解決方案之一。

還有其他的,取決於你的重要。例如,如果子類型的數量在編譯時是有限的並且已知的,那麼您可以爲每個類型指定一個索引,並且可以調用一個二維數組函數指針(不是非常優雅,也不是非常面向對象的但在性能方面效率很高)。

如果擔心函數的數量可能會導致代碼重複,則可以將函數或類中的代碼(類似於CollisionPairBehavior)分解。