2013-10-27 58 views
4

Introdutory note:大約一個月前我開始在大學學習C++。這是一個任務。我們現在正在凝視,並沒有掌握許多先進的概念。我們想象你有一個BookBook是一個Pages*的動態數組。每個Page可以是WrittenPageDrawnPage。如果你想打印所有的Pages你使用virtual method。如果您只想打印DrawnPagesWrittenPages,則必須在Book內部進行某種過濾。怎麼做?現在我發現你需要typeid或某種其他的手段來比較每個Pagesubtype。如果您急於快速簡單地查看接受的答案,請通過@CantChooseUsernames。它對我的問題很好。如果你有更多的專業知識,我想聽聽你對@n.m新答案的看法。 。如果你認爲它給討論帶來了新的和有意義的東西,不要讓目前接受的答案阻止你評論或發佈你自己的答案。如何在C++中傳遞一個類(不是對象)作爲參數


原題:

我有一個類MyObj中這是TheseObj和ThoseObj的超類。

Class TheseObj : public MyObj { 
} 

Class ThoseObj : public MyObj { 
} 

我有一個包含一個std其他類:: vector的使用中,我想只列出TheseObj指針MyObj中的實例和非靜態方法:

Class MyClass { 
    private: 
    vector<MyObj*> vec; 

    public: 
    void listTheseObj() { 
     for each (myObj* obj in vec) { 
      if(typeid(*obj) == typeid(theseObj)) { 
       cout << *obj << endl; 
      } 
     } 
    } 
} 

所有的運營商都已經正確超載。

This works great。現在問題是我有更多的地方需要做同樣的事情,所以我需要一個可以接收GENERIC向量和類TYPE的模板方法,以便我可以執行類似以下操作:

listObjects(class_name_here, vec); 

我設法創造:

template <class T> 
void listObjectsOfOneType(const type_info& class_name_here, const vector<T*>& vec) { 
    for each (T* obj in vec) { 
     if(typeid(*obj) == typeid(class_name_here)) { 
      cout << *obj << endl; 
     } 
    } 
} 

但我不知道:

  1. 如果模板的方法是正確的
  2. 我怎樣才能把它叫做

希望我已經說清楚了,非常感謝您的提前。

+7

「這很好用」 - 也許對於非常小的值很好。您應該使用虛函數來運行子類型特定的代碼。 'typeid'基本上從來都不是正確的用法。 –

+0

如何發佈一些更精緻的東西?如果你可以做一些很棒的例子。 –

+0

我不知道你真正的問題是什麼。它真的列出了一些特定類型的對象,或其他? –

回答

4

我可能會避免使用TypeID ..雖然,我不知道你想實現什麼,但是這是我相信你所要求的:

#include <iostream> 
#include <vector> 
#include <typeinfo> 

template <class T, class U> 
void ListObjects(std::vector<U*> &vec) 
{ 
    for (U* obj : vec) 
    { 
     if (typeid(*obj) == typeid(T)) 
     { 
      obj->Print(); 
      std::cout<<"\n"; 
     } 
    } 
} 

class Parent 
{ 
    public: 
     Parent() {std::cout<<"Parent Constructed\n";} 
     virtual ~Parent() {std::cout<<"Parent Destructed\n";} 

     virtual void Print(){std::cout<<"Parent\n";} 
}; 

class Brother : public Parent 
{ 
    public: 
     Brother(){std::cout<<"Brother Constructed\n";} 
     virtual ~Brother(){std::cout<<"Brother Destructed\n";} 
     void Print() override {std::cout<<"Brother\n";} 
}; 

class Sister : public Parent 
{ 
    public: 
     Sister(){std::cout<<"Sister Constructed\n";} 
     virtual ~Sister(){std::cout<<"Sister Destructed\n";} 
     void Print() override {std::cout<<"Sister\n";} 
}; 

int main() 
{ 
    std::vector<Parent*> Objects; 
    Objects.push_back(new Parent()); 
    Objects.push_back(new Brother()); 
    Objects.push_back(new Sister()); 
    std::cout<<"\n"; 

    ListObjects<Parent>(Objects); 
    ListObjects<Brother>(Objects); 
    ListObjects<Sister>(Objects); 

    for (Parent* c : Objects) 
    { 
     delete c; 
    } 
} 

它打印:

  • 家長構建
  • 家長構建
  • Brother構造
  • 構建的父
  • 構建的構建

  • 兄弟
  • 姐妹

  • 父破壞

  • 兄弟破壞
  • 父破壞
  • 姐妹破壞
  • 父破壞

  • 過程返回0(爲0x0)執行時間:0.066小號

  • 按任意鍵繼續。

很多評論都在告訴你,因爲我們不知道你想要使用的工具不使用typeid的。然而,我們的意思是什麼「沒有必要typeid的」假設我們知道什麼你想要的,那麼下面是有效的:在上面

#include <iostream> 
#include <vector> 
#include <typeinfo> 

template <class T> 
void ListObjects(std::vector<T*> &vec) 
{ 
    for (T* obj : vec) 
    { 
     //TypeID isn't needed here because the virtual call will figure out which class's << operator to call. 

     //If each class has a print function, it can also figure out which class's print function to call.. 
     //obj->Print(); //works too because each class has a print func. 
     std::cout<<*obj<<"\n"; //Works because each class has an overloaded << operator. 
    } 
} 

class Parent 
{ 
    protected: 
     virtual void Print(std::ostream& os) const {os<<"Parent\n";} 

    public: 
     Parent() {std::cout<<"Parent Constructed\n";} 
     virtual ~Parent() {std::cout<<"Parent Destructed\n";} 

     friend std::ostream& operator << (std::ostream &os, const Parent &p); 
}; 

std::ostream& operator << (std::ostream &os, const Parent &p) 
{ 
    p.Print(os); 
    return os; 
} 

class Brother : public Parent 
{ 
    protected: 
     void Print(std::ostream& os) const override {os<<"Brother\n";} 

    public: 
     Brother(){std::cout<<"Brother Constructed\n";} 

     virtual ~Brother() {std::cout<<"Brother Destructed\n";} 
}; 

class Sister : public Parent 
{ 
    protected: 
     void Print(std::ostream& os) const override {os<<"Sister\n";} 

    public: 
     Sister(){std::cout<<"Sister Constructed\n";} 
     virtual ~Sister(){std::cout<<"Sister Destructed\n";} 
}; 

int main() 
{ 
    std::vector<Parent*> Objects; 
    Objects.push_back(new Parent()); 
    Objects.push_back(new Brother()); 
    Objects.push_back(new Sister()); 
    std::cout<<"\n"; 

    ListObjects(Objects); //NOTICE we all template types are now inferred. 

    for (Parent* c : Objects) 
    { 
     delete c; 
    } 
} 

注意,因爲這種呼叫是虛擬的,該代碼打印相同,使用的typeid和代碼不再需要你在鍵入任何代碼模板的大括號。推斷是因爲我們不再需要使用typeid進行比較。


現在,因爲你所要求的前面的代碼與模板是一個參數,而不是,則:

template <class T, class U> 
void ListObjects(std::vector<U*> &vec) 
{ 
    for (U* obj : vec) 
    { 
     if (typeid(*obj) == typeid(T)) 
     { 
      obj->Print(); 
      std::cout<<"\n"; 
     } 
    } 
} 

將成爲:

template<typename T> 
void ListObjects(std::vector<T*> &vec, const std::type_info &type) 
{ 
    for (T* obj : vec) 
    { 
     if (typeid(*obj) == type) 
     { 
      std::cout<<*obj<<"\n"; 
     } 
    } 
} 

和你使用它像:ListObjects(Objects, typeid(Child));

再次,所有這些給你完全相同的結果。這一切都取決於您的需求/使用情況。我們不完全知道你想達到什麼。這些應該可以幫助你。

+0

謝謝!這幾乎是我要找的。問題是你需要做一些事情,如[ListObjects (Objects); ]。這樣我就不得不在diammond推理中鍵入superclass_name,這對我來說沒有意義,因爲我已經知道vector包含類T的元素。任何替代方法? –

+0

你好,先生。我剛剛編輯了代碼。現在只需輸入:'ListObjects (Objects);'而不是'ListObjects (Objects);'。這樣,它就可以通過矢量自動計算出(推斷)另一種類型。我所做的只是交換模板參數。我相信這是一樣好的嗎?除非你有其他想法。在這種情況下,我不確定你的意思? – Brandon

+0

也可以建議使用虛擬功能,而不是選擇這種方式。 – phoad

2

除非你正在做這個測試的一部分,只是爲了弄清楚代碼中的某些部分,我同意評論者的意見,這是一個非常糟糕的主意。

template < typename T > 
void listObjects(const std::vector<MyObj*>& vec) { 
    for (MyObj* obj: vec) { 
     if (typeid(*obj) == typeid(T)) { 
      // one of the two below, depending on what your op<< looks like 
      std::cout << *obj << std::endl; 
      std::cout << dynamic_cast<T&>(*obj) << std::endl; 
     } 
    } 
} 

void calledLikeThis(const std::vector<MyObj*>& vec) { 
    listObjects<TheseObj>(vec); 
} 
+0

感謝您的回答。你迫使矢量是MyObj *,我不想要。該方法應該收到一個Template類型的向量。我設法做[listObjects (vec); ]但是這樣我就不得不在diammond推理中鍵入superclass_name,這對我來說沒有意義,因爲我已經知道vector包含類T的元素。 –

+0

您可能知道,但編譯器不會。所有的模板知道你有一個類型X,並且你正在檢查它是否也是一個Y類型。沒有理由假設一個是另一個的子類。畢竟,這種關係可能被顛倒,或者可能涉及多重繼承。 –

+0

該矢量明確宣佈爲矢量 vec;在我的班級裏面。當然,有很多很酷的事情正在進行,但是矢量是這種類型的,所以編譯器已經知道了。我創建了一個簡單的模板接收vec並給我typeid(vec).name的方法。調用方法(vec)時可以正常工作;沒有鑽石推理。 –

0

以這種方式使用typeid違反了Liskov替代原則。LSP粗略地說,如果你的函數與X類的對象一起工作,它也應該與X的任何子類的(一些)對象一起工作。你的listTheseObj函數將只列出類型的對象類型TheseObj,但不能的任何亞型。

對於調試目的或基礎架構/框架項目,您可以實現反射或序列化等服務並使用typeid(obj)爲它們編制索引。但業務邏輯不應該這樣工作。用戶對於任何技術原因都不感興趣,會讓你將TheseObj分成幾個子類型;他們想要他們的類型的概念(如果有的話)。

如果你只想打印TheseObj任何子類類型的對象,你可以更換

typeid(*obj) == typeid(TheseObj) 

dynamic_cast<TheseObj*>(obj) != 0 

模板化的版本是這樣的:

template<typename T, typename U> 
void ListObjects(std::vector<T*> &vec>) 
{ 
    for (T* obj : vec) 
    { 
     if (dynamic_cast<U*>(obj) != 0) 
     { 
      std::cout<<*obj<<"\n"; 
     } 
    } 
} 

值得注意的是if的主體不以任何方式使用該條件。這暗示了將它們分開的可能性。

template<typename T> 
void ActOnObjects(std::vector<T*> &vec>, std::function<bool(T*)> predicate, 
             std::function<void(T*)> action) 
{ 
    for (T* obj : vec) 
    { 
     if (predicate(obj)) 
     { 
      action(obj); 
     } 
    } 
} 

現在,您可以使用RTTI或不使用RTTI過濾任何謂詞。

ActOnObjects(objects, [](T* obj){return dynamic_cast<ThatObj*>(obj) != 0;}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

ActOnObjects(objects, [](T* obj){return obj->isVeryImportant();}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

ActOnObjects(objects, [](T* obj){return obj->isBlue() && obj->isWobbly();}, 
         [](T* obj){std::cout << *obj << std::endl;}); 

此外,請使用迭代器範圍,而不是容器,像任何良好的公民的C + +土地;我把這留作練習。

+0

''dynamic_cast (obj)!= 0'優於'(typeid(* obj)== typeid(U))''的優點是什麼?我不想創建很多邊界討論,我只想說,根據上下文的不同,我認爲'typename'可能不如'class'有效,而不是相反。關於迭代器,我在這種情況下看到的唯一優點是,您可以更輕鬆地更改代碼以適應其他容器,對嗎?對不起,我沒有解決你答案的其餘部分,但說實話我沒有把握這些概念,我需要做一些閱讀,然後才能以更好的方式回覆你。 –

+0

動態轉換將在類*和*的所有派生類上工作,typeid比較不會。關於typename的句子是錯誤的。關於迭代器的陳述是正確的(這可能是一個很大的優勢)。 –

相關問題