2013-10-04 73 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; } 

    }; 

这可能是一个好主意,让列表类构造函数,这将使该列表的用户初始化其分配数据成员(按价值计算)的选项。

你也可以给堆栈一个构造函数,将参加并通过分配器的实例,其内部列表的构造,出于同样的原因。