2016-12-15 38 views
2

我以爲我已經理解了C++中的類模板的概念,但看着我的代碼,我不再那麼肯定了。說我有兩個班,一個非模板家長和模板的孩子,像這樣:什麼是C++處理類模板及其類型的方式?

class MyParent 
{ 
    std::string m_Name; 
    MyParent(std::string Name) : m_Name(Name) { } 
    virtual const std::type_info& GetType() = 0; 
}; 

template <class T> 
class MyChild : public MyParent 
{ 
    T m_Var; 
    const std::type_info& GetType() override 
    { return typeid(m_Var); } 
}; 

現在,在我的代碼我有使用不同類型的MyChild實例的容器,例如:

MyParent* p1 = new MyChild<int>("one"); 
MyParent* p2 = new MyChild<float>("two"); 
MyParent* p3 = new MyChild<double>("three"); 
std::vector<MyParent*> v = {p1, p2, p3}; 

到目前爲止,這很清楚,現在我的困惑開始。假設我有一個函數遍歷這個向量,那麼它需要爲每個元素做一些事情。一些例子,我只是做了(我的問題是不是代碼本身,而是如何應對這樣的情況):

/* ... */ 
for(auto* p : v) 
{ 
    if(p->GetType() == typeid(int)) 
    { 
     int val = p->m_Var; 
     std::list<int> lst = {val, 1, 2, 3}; 
     if(val >= 0) 
      SomeTemplFuncPositive(v * v, lst); // a template variadic function 
     else 
      SomeTemplFuncNegative(v * v * -1, lst); 
    } 
    else if(p->GetType() == typeid(float)) 
    { 
     /* ... now the same block c&p again for float? */ 
    } 
    else if(p->GetType() == typeid(double)) 
    { 
     /* ... and again for double?! */ 
    } 
} 
/* ... */ 

在其他語言如Python比如我有這樣的代碼塊只有一次,但在C + +在我看來,我需要複製整個代碼塊再次爲float,另一次爲double,等等......

我不想責怪C++,如果這是它的方式必須是,那麼確定。我只是想知道,在C++中使用這樣的構造時,這真的是一種正確的方法嗎?

+0

你沒有模板的問題,但與對象。用'typeid'設計是醜陋的。爲什麼存在多態性。 – Stargateur

+1

'if(p-> GetType()== typeid(int))'引入了一些程序編碼(和思考)。將所有代碼從if類中的所有代碼移到'* p'類中。如有必要,請延長MyChild 。 – axiac

+4

事實上,您的子類的行爲不同,並且您必須在類型上進行分支意味着您的設計存在缺陷。你真的想解決什麼問題?就目前來看,這是一個XY問題,如下所示:我選擇了錯誤的解決方案來解決我的問題,現在這種語言正在打擊它,直到我要求互聯網如何繼續。每當我到達這個地步時,總會有一些向後的選擇讓我在那裏,從根本上改變我的方法改善了很多事情。只是我的2美分...... – rubenvb

回答

6

virtual -ity需要進行調整:

class MyParent 
{ 
    std::string m_Name; 
    MyParent(std::string Name) : m_Name(Name) { } 
    virtual void doSomething()=0; 
}; 

template <class T> 
class MyChild : public MyParent 
{ 
    T m_Var; 
    void doSomething() override 
    { 
     T val = this->m_Var; 
     std::list<T> lst = {val, 1, 2, 3}; 
     if (val >= 0) 
      SomeTemplFuncPositive(v * v, lst); 
     else 
      SomeTemplFuncNegative(v * v * -1, lst); 
    } 
}; 

現在,您可以調用doSomething()通過其基類:

for(auto* p : v) 
{ 
    p->doSomething(); 
} 

一切都寫一次。

+0

這假設'MyChild'可以直接訪問SomeTemplFuncPositive()和SomeTemplFuncNegative()函數。在原始代碼中,這些函數不在MyChild中。如果需要的話,你只需要將函數指針傳遞給'doSomething()',以便知道該調用什麼。 –

0

如果你需要從Base到Child進行強制轉換,那麼你的代碼很可能是錯誤的(至少在C++中)。當你存儲指向派生的指針時,你應該確保你不需要公共訪問他們特定的成員(但虛擬方法仍然可以這樣做)。在這種情況下,列表應該在派生中聲明並以T作爲模板參數。

而且,你並不需要的GetType功能,您可以用dynamic_cast<DerivativeClass*>(baseClassPtr),它返回正確的指針,如果鑄造是確定的,而nullptr如果鑄造不能這樣做。

相關問題