2011-08-19 91 views
1

我真的不知道如何制定我的問題,但這裏是我試圖解決這一難題:從不同類型的模板類創建對象

if (config.a) 
    myObject = new Object<DummyInterface>(); 
else 
    myObject = new Object<RealInterface>(); 

所以任務是創建一個一個對象如果在config中指定了虛擬接口,則使用真實接口類。 那麼我該如何聲明myObject? 有幾個選項,我可以有對象類從抽象類派生無模板:即:

class Base 
{ 
    ... 
} 

template <class T> 
class Object : public Base 
{ 
... 
} 

然後,我可以宣佈myObject的是:

Base* myObject; 

但現在的問題是:如果我的對象類聲明非虛方法:

template <class T> 
class Object : public Base 
{ 
public: 
    T getInterface() { return myInterface;} 
private: 
    T myInterface; 
} 

我不能把它像這樣:

myObject->getInterface() 

,我不能做動態轉換,因爲我不知道是什麼類型,直到運行時...

任何建議如何解決呢?也許有另一種解決方案?

+4

如果您需要將接口投射到實施中,則出現問題。嘗試審查你的課程設計。 –

回答

2

單程就是使用訪問者模式。這樣一來,你的基類可以實現visit()方法和派生實例可以覆蓋...

例如..

SomeComponent 
{ 
    template <typename T> // I'm being lazy here, but you should handle specific types 
    void handle(T& cInst) 
    { 
    // do something 
    } 
}; 

class Base 
{ 
public: 
    virtual void visit(SomeComponent& cComp) = 0; 
}; 

template <class T> 
class Object : public Base 
{ 
public: 
    virtual void visit(SomeComponent& cComp) 
    { 
    cComp.handle(*this); 
    } 
}; 

現在你可以做到這一點

SomeComponent c; 
Base* obj = new Object<int>; 
obj->visit(c); 

而且c會得到正確的類型。

+0

比我的好得多:) –

0
if (config.a) 
    myObject = new Object<DummyInterface>(); 
else 
    myObject = new Object<RealInterface>(); 

這種構造在多態性方面是不正確的。 兩個模板實例化是兩個不同的類。最好的情況是,當你有這樣的事情:

template <class T> SomeClass: public SomeBaseClass 
{ 
}; 
......... 
SomeBaseClass* myObject; 

但它帶給你沒有利潤。 最簡單和正確的解決方案是虛擬功能。訪問者模式似乎也很有用。

0

我實際上認爲訪客模式在這裏會被濫用。相反,這是一種典型的開關式代碼氣味,最好由多態性來處理。

當你說「如果一個派生類有一個額外的方法來調用」,那是假設一個特定的設計。這不是功能要求。功能要求是「如果創建的兩個對象中的一個必須在事件Y期間執行行爲X」。爲什麼這是不同的?因爲有很多方法可以實現這一點,所以不需要更多的接口(儘管也許有更多的方法)。

讓我來舉個例子。

你有你的工廠

std::map<ConfigValue, Generator> objectFactory_; 

,您已經註冊了一堆發電機爲(可能在類的構造函數)

RegisterGenerator(configValueA, DummyGenerator); 
RegisterGenerator(configValueB, RealGenerator); 
... 

而且你要創建其中的一個部分點對象。

shared_ptr<Base> GetConfigObject(ConfigFile config) 
{ 
    return objectFactory_[config.a](); 
} 

然後你要使用的對象來處理一個事件,你可以做

void ManagingClass::HandleEventA() 
{ 
    theBaseObjectReturned->HandleEventAThroughInterfaceObject(this); 
} 

注意我是如何通過的這個指針。這意味着如果你有一個對象不想做任何事情(比如進行額外的行爲調用),你的管理類可能會提供,它不需要使用它。

Object<DummyInterface>::HandleEventAThroughInterfaceObject(ManagingClass *) 
{ 
    // just do dummy behavior 
} 

,然後如果你想做些額外的事情(調用一個新的行爲),它可以做到這一點通過該指針在RealInterface

Object<RealInterface>::HandleEventAThroughInterfaceObject(ManagingClass * that) 
{ 
    that->DoExtraBehavior(); 
    // then dummy - or whatever order 
    // you could even call multiple methods as needed 
} 

這與打交道時,你應該始終採取的基本途徑多態性。除了通過調用虛擬調度之外,不應該爲不同的類型使用兩種不同的代碼路徑。你不應該有兩個不同的代碼塊,一個調用方法A,B和C,另一個調用A和D來處理基礎對象,具體取決於類型。相反,總是讓派生的對象完成確定要做什麼的工作 - 因爲他們知道他們是誰。如果您需要在管理對象中執行某些操作,請傳遞一個指針以供它們使用。

+0

要添加更多點:如果您希望我在HandleEventAThroughInterfaceObject中提到的行爲使用類型T - 您可以在那裏做到這一點。如果您需要將類型T發送到ManagingClass,則可以將其發送到那裏(可能調用模板函數)。你需要做的一切,在你知道類型信息的地方做。這是一個比訪問者模式要解決的更普遍的問題。你並不總是需要給經理打電話,你沒有建立一個添加更多虛擬方法的方法等。這只是基本的多態設計。 – ex0du5