2013-10-04 115 views
1

在我的業餘時間(作爲一個學習練習),我一直在用C++創建一些模板化的容器和分配器,類似於作爲標準模板庫的一部分提供的模板。C++模板類循環依賴

到目前爲止,我所做的容器單向鏈表,雙向鏈表,棧,隊列和。堆棧和隊列都使用單鏈表作爲它們的內部結構,因爲我同時存儲頭指針和尾指針。

現在到了我的第一個allocator類的池分配器。內部它使用我的籌碼對象獲取和釋放預分配的對象之一。我現在想用這個Pool Allocator與我的單鏈表和雙鏈表進行結合,以便預先分配存儲數據的內部節點對象。在我看來,現在這個樣子創造了我的項目循環依賴問題。

我在非模板類上解決像這樣的依賴性問題的常用方法通常涉及向前聲明,指針和將實現分割爲cpp文件。這個問題似乎出現了,因爲我無法將模板代碼聲明和實現分離到各自的.h和.cpp文件中。

進一步參考的一些代碼:

SinglyLinkedList.h:

#include "PoolAllocator.h" //Adding this line creates a compile error 

template<typename T> class SinglyLinkedList 
{ 
private: 
    Node<T> *_Head, *_Tail; 
public: 
    void PushFront(T *obj) 
    { 
      //Allocate new node object and set it as _Head 
    } 

    void PushBack(T *obj) 
    { 
      //Allocate new node object and set it as _Tail 
    } 

    T *PopFront() 
    { 
      //Remove _Head and return node data 
    } 
}; 

Stack.h:

#include "SinglyLinkedList.h" 

template<typename T> class Stack 
{ 
private: 
    SinglyLinkedList<T> _List; 
public: 
    void Push(T *obj) 
    { 
      _List.PushFront(obj); 
    } 

    T *Pop() 
    { 
      return _List.PopFront(); 
    } 
}; 

PoolAllocator.h:

#include "Stack.h" 

template<typename T> class PoolAllocator 
{ 
private: 
    Stack<T> _Pool; 
public: 
    void Initialize(unsigned int capacity) 
    { 
      //Dynamically allocate a bunch of T and push them onto _Pool 
    } 

    T *Acquire() 
    { 
      //Remove an item from _Pool and return it 
    } 

    void Release(T *obj) 
    { 
      //Push the object back onto the _Pool 
    } 

    void Dispose() 
    { 
      //Free all memory from _Pool 
    } 
}; 

我是有點不確定最好的解決方法這個問題。我能想到的唯一方法是讓Pool Allocator不使用我的任何容器類。我想我可以創建一個獨立於分配器類的內部鏈表類,但這似乎是不必要的代碼重複。

如果有人對此有任何見解,我會很高興聽到它。我希望我已經完全覆蓋了所有內容,並提供了可接受的代碼示例如果有任何缺少的信息,請讓我知道。提前致謝。

+1

[解決模板類之間的循環依賴關係]可能的重複(http://stackoverflow.com/questions/3353831/resolving-a-circular-dependency-between-template-classes) –

+0

我忘記把標頭警衛放入我的例子,但他們在那裏。我也試過這個鏈接,沒有運氣:( –

+0

即使你可以得到這個編譯,我不明白它是如何工作的 - 爲了在'PoolAllocator'內部填充'Stack',你將東西推到它上面,調用'List.PushBack()',它會去到一個未初始化的'PoolAllocator'來獲得內存,右 –

回答

1

如果我理解正確的話,你有兩個問題:

  1. 就相當於告訴一個鏈表包含池分配器編譯器,和一個游泳池分配器包含一個堆棧(其中包含一個鏈接列表),因此實例化這些對象中的任何一個都會告訴編譯器分配一組無限遞歸的對象。由於您的列表是從您的池分配器分配的,並且您的池分配器是從列表中分配的,因此實際上沒有任何東西從免費存儲區分配節點(例如,運算符new和delete)。

循環依賴是錯誤的邏輯。你需要打破其中一個依賴關係。由於鏈表對你的池分配器的依賴關係是通過設計的,所以你需要打破其他依賴關係:池分配器包含一個包含一個池分配器的鏈表(在堆棧數據成員中)。最後一部分是你的問題的根源。要做到這一點

的一種方法是使分配器輸入您的容器類的模板參數,然後做一個特殊的allocator類只是爲池分配器的內部堆棧。所以你有這些類型:

template <typename T> 

    class Node { /* ... */ }; 


template <typename T, class A = PoolAllocator <T>> 

    class SinglyLinkedList { 

    A _Allocator; 

    Node <T> * _Head; 
    Node <T> * _Tail; 

    /* ... */ 

    }; 


template <typename T, class A = PoolAllocator <T>> 

    class DoublyLinkedList { 

    A _Allocator; 

    Node <T> * _Head; 
    Node <T> * _Tail; 

    /* ... */ 

    }; 


template <typename T, class A = PoolAllocator <T>>  

    class Stack { 

    SinglyLinkedList <T, A> _List; 

    /* ... */ 

    }; 


template <typename T> 

    class PoolAllocator <T> { 

    Stack <T, FreeStoreAllocator <T>> _Pool; 

    /* ... */ 

    }; 


template <typename T> 

    class FreeStoreAllocator { 

    public: 

    Node <T> * AllocateNode() const { return new Node <T>; } 

    void DeallocateNode (Node <T> * p) const { delete p; } 

    }; 

這可能是一個好主意,讓列表類構造函數,這將使該列表的用戶初始化其分配數據成員(按價值計算)的選項。

你也可以給堆棧一個構造函數,將參加並通過分配器的實例,其內部列表的構造,出於同樣的原因。