2012-03-07 87 views
1

我寫一個工人階級的模板,這也可能只是一個愚蠢的問題,但如果我有一個模板結構(鏈表)到可能持有對象的指針,然後我怎麼知道他們正在被刪除,還是他們在哪裏指針?確定模板類型是動態

例如:LinkedList的將在第2種方式使用在此程序

一個指針指向類的東西的物體被放置在節點內的鏈表

枚舉被放置在節點內的內部內一個linkedList

我知道節點正在被刪除,但我怎麼知道節點中的東西是一個指針,以便它可以被刪除,而不僅僅是一個空引用對象?

+0

您是否想製作不同類型的鏈接列表? – Xeo 2012-03-07 19:26:18

+0

爲了確保我正確理解這一點,你想有一個LinkedList​​3210,然後能夠推動Blah *進入它並讓LinkedList自動銷燬並釋放鏈表的內容指向的所有對象?這似乎是班上不應該做的事情。如果你想保持一個對象存活,但是你已經將它推入列表並且該列表已被刪除,該怎麼辦?也許你正在尋找某種智能指針? (像Boost智能指針。) – Corbin 2012-03-07 19:28:16

+0

@Xeo是的,但我的其中一種類型將枚舉(靜態),另一種類型將指向事物(動態)。結構已經存在,我正在努力,雖然現在將它轉換爲模板 – gardian06 2012-03-07 19:28:49

回答

4

您可以專注基於對象的類型的節點,併爲指針專業化,創建節點型,妥善分配,並刪除節點管理的指針析構函數。

例如:

//general node type for non-pointer types 
template<typename T> 
struct linked_list_node 
{ 
    T data; 
    linked_list_node<T>* next; 

    linked_list_node(const T& d): data(d), next(NULL) {} 
    ~linked_list_node() {} 
}; 

//specialized version for pointer types 
template<typename T> 
struct linked_list_node<T*> 
{ 
    typedef void (*deleter)(T*); 

    T* data; 
    linked_list_node<T>* next; 
    deleter d_func; //custom function for reclaiming pointer-type 

    linked_list_node(const T& d): data(new T(d)), next(NULL), d_func(NULL) {} 

    linked_list_node(const T& d, deleter func): data(new T(d)), 
               next(NULL), d_func(func) {} 
    ~linked_list_node() 
    { 
     if(d_func) 
      d_func(data); //execute custom function for reclaiming pointer-type 
     else 
      delete data; 
    } 
}; 

然後,您可以通過創建linked_list_node類型的實例時傳遞正確的模板參數實例化不同的版本。例如,

linked_list_node<MyPtr*> node(FooPtr); //creates the specialized ptr version 
linked_list_node<MyEnum> node(FooEnum); //creates a non-ptr version of the node 
+0

然後我是否做某種情況切換以確定使用哪一個 – gardian06 2012-03-07 19:38:34

+0

不,只需使用指針類型或枚舉類型實例化模板參數即可。例如,'linked_list_node ',或'linked_list_node '。 – Jason 2012-03-07 19:43:58

+2

但是不要專注於整個列表數據結構。相反,只需創建一個幫助器函數(或類),該函數在需要釋放元素時調用,助手將按類型進行專門化。你也可以專門針對特定的類型,即'FILE *'可能會調用'fclose'而不是'delete'。 – 2012-03-07 19:49:24

1

模板專業化是最好的答案,並且將工作做好,只要你不混合類型的節點。但是,如果你想混合鏈接節點的類型,讓我告訴你如何去做。首先,沒有簡單的模板解決方案。由於嚴格的類型限制,您必須一起鍵入您的鏈接節點。

甲相當普遍的解決方案是構建變體類(其可以容納具有變化類型的一個值,並且總是知道哪一個)。例如,Qt有一個QVariant類。 Boost有boost::any

這是一個完整的示例實現,它使用可以容納任何類型的自定義變體類。我可以處理你建議的對象指針和枚舉,但可以擴展來保存更多。

這將打印「刪除目標文件」一旦一個例子:

#include <iostream>                                                  

    int                                                      
    main(int argc, char **argv)                                                
    {                                                       
     LinkedList<VariantExample> elementObj(new ExampleObj);                                         

     LinkedList<VariantExample> elementEnum(enumOne);                                          

     elementEnum.setNext(elementObj);                                              
    } 

// VariantExample class. Have a look at [QVariant][4] to see how a fairly 
// complete interface could look like. 

     struct ExampleObj                                                   
     {                                                       
     };                                                      

     enum ExampleEnum                                                   
     {                                                       
      enumOne,                                                    
      enumTwo                                                    
     };                                                      

     struct VariantExample                                                  
     {                                                       
      ExampleObj* obj;  // or better boost::shared_ptr<ExampleObj> obj                                                  
      ExampleEnum en;                                                   

      bool is_obj;                                                   
      bool is_enum;                                                   

      VariantExample() : obj(0), is_obj(false), is_enum(false) {} 

      // implicit conversion constructors 

      VariantExample(ExampleObj* obj_) : is_obj(true), is_enum(false)                                     
      { obj = obj_;                                                   
      }                                                      

      VariantExample(ExampleEnum en_) : obj(0), is_obj(false), is_enum(true)                                       
      { en = en_;                                                    
      }                                                      

      // Not needed when using boost::shared_ptr above 

      void                                                     
      destroy()                                                    
      {                                                      
      if(is_obj && obj)                                                   
       {                                                     
       std::cout << "delete obj" << std::endl;                                           

       delete obj;                                                  
       }                                                     
      }                                                      

     };    


// The linked list template class which handles variant classes with a destroy() 
// method (see VariantExample). 

    template                                                     
    <                                                       
     typename _type_ = VariantExample                                                  
    >                                                       
    struct LinkedList                                                   
    {                                                       
     LinkedList* m_next;                                                  

     _type_ m_variant;                                                  

     explicit                                                    
     LinkedList(_type_ variant_) : m_next(0), m_variant(variant_){ }                                        

     void                                                     
     setNext(LinkedList& next_){ m_next = &next_; }                                          

     // Not needed when using boost::shared_ptr above 

     ~LinkedList()                                                   
     {                                                      
     m_variant.destroy();                                                 
     }                                                      
    };                                                       

因爲調用一次當鏈表的析構函數被調用elementObj的破壞方法,輸出的「刪除目標文件」中出現一次。同樣,由於您對刪除/所有權非常具體,因此此示例具有銷燬方法/接口。它將在LinkedList類的析構函數中顯式調用。一個更好的所有權模式可以用ie來實現。 boost::shared_ptr。那麼你不需要手動銷燬它。順便說一下,它有助於閱讀conversion constructors

// the first parameter becomes boost::shared_ptr<ExampleObj>(new ExampleObj)) 
    // and is deleted when LinkedList is destroyed. See code comments above. 

    LinkedList<> elementObj(new ExampleObj);                                         

最後請注意,您必須有一個變體類才能容納您的LinkedList鏈中可能出現的所有類型。最後,由於「下一個」指針類型,兩個不同的LinkedList Variant類型將無法工作。這將不兼容。

腳註: 類型約束如何防止一個簡單的解決方案嗎?想象一下,鏈接節點的「下一個」指針類型不僅僅是裸模板名稱,它是一個快捷方式,但實際上還包括模板參數 - 最終作爲編譯器用來判斷類型兼容性的類型符號。

+0

只要你用不同類型的節點設置node.next,Jason的例子就不應該編譯 – muenalan 2012-03-08 10:27:51

+0

帶模板的想法是強制執行一個類型約束...所以它不會編譯的事實不是錯誤,而是表示你已經打破了類型約束規則,這在編譯而不是運行時要好得多。 – Jason 2012-03-08 14:26:16

+0

是的,同意:沒有模板廢話應潛入運行時;)所以,我的觀點:寬鬆類型約束的變種。然而,使用模板的想法是爲了防止代碼重複而不違反語言限制;包括類型約束。見本Voigts評論你的答案。 – muenalan 2012-03-10 11:22:59