2010-05-16 15 views
0

我覺得這個問題的答案很簡單,但我真的無法找到它。所以這裏有雲:根據對象的動態類型調用一組重載函數

假設你有以下類:

class Base; 
class Child : public Base; 

class Displayer 
{ 
public: 
    Displayer(Base* element); 
    Displayer(Child* element); 
} 

另外,我有一個Base* object可能指向任何類Base的實例或類Child的一個實例。

現在我想創建基於元素上的Displayer通過object指出,但是,我要挑構造的正確版本。由於我現在有它,這將做到這一點(我是一個有點模糊我的C++在這裏,但我認爲這最清楚的方式)

object->createDisplayer(); 

virtual void Base::createDisplayer() 
{ 
    new Displayer(this); 
} 

virtual void Child::createDisplayer() 
{ 
    new Displayer(this); 
} 

這工作,然而,與此問題:

BaseChild是應用系統的一部分,而Displayer是GUI系統的一部分。我想獨立於應用程序系統構建GUI系統,以便更換GUI。這意味着BaseChild不應該知道Displayer。但是,我不知道如何在不讓Application類知道GUI的情況下實現這一點。

我錯過了一些非常明顯的東西,還是我嘗試了一些不可能的事情?

編輯:我錯過了原來問題中的一部分問題。這在GUI代碼中發生得相當深,提供了這一個GUI獨有的功能。這意味着我希望BaseChild類完全不知道有關該呼叫 - 而不僅僅是隱藏呼叫的內容

+0

我試圖讓標題更具表現力。我希望你同意。 – sbi 2010-05-16 21:18:36

+0

我很喜歡這個標題,謝謝 – Jasper 2010-05-16 21:34:35

回答

3

這似乎是雙派遣的經典場景。避免雙重調度的唯一方法是切換您應該避免的類型(if(typeid(*object) == typeid(base)) ...)。

你可以做的是讓回撥機制通用的,以使應用程序不必知道GUI的:

class app_callback { 
    public: 
    // sprinkle const where appropriate... 
    virtual void call(base&) = 0; 
    virtual void call(derived&) = 0; 
}; 

class Base { 
    public: 
    virtual void call_me_back(app_callback& cb) {cb.call(*this);} 
}; 
class Child : public Base { 
    public: 
    virtual void call_me_back(app_callback& cb) {cb.call(*this);} 
}; 

然後,您可以使用此機器是這樣的:

class display_callback : public app_callback { 
    public: 
    // sprinkle const where appropriate... 
    virtual void call(base& obj) { displayer = new Displayer(obj); } 
    virtual void call(derived& obj) { displayer = new Displayer(obj); } 

    Displayer* displayer; 
}; 

Displayer* create_displayer(Base& obj) 
{ 
    display_callback dcb; 
    obj.call_me_back(dcb); 
    return dcb.displayer; 
} 

你必須有層次結構中的每個類一個app_callback::call()功能,你將不得不增加每次一到每個回調你添加一個類層次結構。
因爲在你的情況下,只需調用base&是可能的,也是如此,當你忘記重載這些函數在回調類中的編譯器將不會引發錯誤。它只會打電話給一個採取base&。那很糟。

如果需要,可以使用CRTP將每個類的相同代碼call_me_back()移動到私有繼承類模板中。但是,如果你只有六門課,它並不能真正增加這麼多的清晰度,它需要讀者理解CRTP。

+0

雖然你的代碼是我的問題的解決方案,但它不是我正在尋找的......問題在於'object-> createDisplayer();'在GUI代碼中很深,提供了自己的功能這個圖形用戶界面 - 這意味着我不希望遊戲有任何可能的回調... – Jasper 2010-05-16 21:53:05

+0

@Jasper:我不認爲你可以避免這種情況。至少不是不採取切換式,這更糟糕。無論如何,我的建議會在您的層次結構的類中創建一個通用鉤子,任何代碼都可以鉤入該層級。 'display_callback'可能在GUI代碼中很深,遊戲不需要知道它。它只知道通用的'app_callback'類型。 – sbi 2010-05-16 22:08:11

+1

這種模式通常被稱爲「訪問者」,我相信它就是你真正想要的:下層只理解通用的「訪問者」(回答中的回調),並且你可以創建具體的圖形顯示創建者訪問者在GUI層中,僅使用較低層進行分派。 – 2010-05-16 23:34:39

0

讓應用程序在系統代碼上設置工廠接口。這是一個黑客入侵的方式來做到這一點。顯然,將這​​些更改應用於您自己的偏好和編碼標準。在某些地方,我正在內聯類聲明中的函數 - 只是爲了簡潔起見。

// PLATFORM CODE 
// platformcode.h - BEGIN 
class IDisplayer; 
class IDisplayFactory 
{ 
    virtual IDisplayer* CreateDisplayer(Base* pBase) = 0; 
    virtual IDisplayer* CreateDisplayer(Child* pBase) = 0; 
}; 

namespace SystemDisplayerFactory 
{ 
    static IDisplayFactory* s_pFactory; 
    SetFactory(IDisplayFactory* pFactory) 
    { 
     s_pFactory = pFactory; 
    } 

    IDisplayFactory* GetFactory() 
    { 
     return s_pFactory; 
    } 
}; 
// platformcode.h - end 

// Base.cpp和Child.cpp落實 「CreateDisplayer」 方法如下

void Base::CreateDisplayer() 
{ 
    IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this); 
} 


void Child::CreateDisplayer() 
{ 
    IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this); 
} 

//在應用程序代碼,這樣做:

#include "platformcode.h" 

    class CDiplayerFactory : public IDisplayerFactory 
    { 
     IDisplayer* CreateDisplayer(Base* pBase) 
     { 
      return new Displayer(pBase); 
     } 

     IDisplayer* CreateDisplayer(Child* pChild) 
     { 
      return new Displayer(pChild); 
     } 
    } 

然後某處在應用程序初始化(主或WinMain)初期,請說明以下內容:

CDisplayerFactory* pFactory = new CDisplayerFactory(); 
SystemDisplayFactory::SetFactory(pFactory); 

這將使您的平臺代碼不必知道「顯示器」是什麼的混亂細節,您可以稍後實現模擬版本的IDisplayer,以獨立於渲染系統測試Base和Child。

此外,IDisplayer(方法未顯示)成爲平臺代碼公開的接口聲明。你的「Displayer」實現是一個繼承自IDisplayer的類(在你的應用代碼中)。

+0

這確實解決了我提出的問題,但有一點問題:這一切都發生在GUI代碼深處,提供了其他GUI可能根本不提供的功能...... – Jasper 2010-05-16 21:54:42

+0

我可能已經將您的平臺對象與您的應用程序對象混淆了。但沒關係。關鍵在於接口提供了不應該彼此瞭解的組件之間的邏輯抽象。對於其他「根本不可能提供」的gui,您仍然可能不得不提取抽象以使其與代碼的其餘部分兼容。識別與另一個gui的共同點,並使用通用接口編寫抽象層。 – selbie 2010-05-16 23:11:38

相關問題