在我的业余时间(作为学习练习),我一直在用C ++创建一些模板化容器和分配器,类似于标准模板库中提供的那些。
到目前为止,我制作的容器是单链表,双链表,堆栈和队列。 Stack和Queue都使用单链接列表作为内部结构,因为我存储了头指针和尾指针。
现在是我的第一个分配器类,Pool Allocator。在内部,它使用我的一个Stack对象来获取和释放预先分配的对象。我现在想将这个Pool Allocator与我的单链接列表和双链表一起使用,以预先分配存储数据的内部Node对象。在我看来,现在在我的项目中创建了一个循环依赖问题。
我在非模板化类中解决此类依赖性问题的常规方法通常涉及前向声明,指针以及将实现拆分为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不使用我的任何容器类。我想我可以创建一个内部链接列表类,它对于allocator类是独占的,但这似乎是不必要的代码重复。
如果有人对此有任何见解,我会很高兴听到它。我希望我能够彻底涵盖所有内容并提供一个可接受的代码示例。如果有任何遗漏信息,请告诉我。提前谢谢。
答案 0 :(得分:1)
如果我理解正确,你有两个问题:
您实际上告诉编译器链表包含池分配器,池分配器包含堆栈(包含链表),因此实例化这些对象中的任何一个都会告诉编译器分配一组无限递归的对象。
由于您的列表从池分配器分配,并且池分配器从列表中分配,因此实际上没有任何节点从免费存储中分配节点(例如运营商new和delete)。
循环依赖是错误的逻辑。您需要打破其中一个依赖项。由于链接列表&#39;您的池分配器依赖于设计,您需要打破其他依赖关系:池分配器包含一个包含池分配器的链表(在堆栈数据成员中)。最后一部分是你问题的根源。
执行此操作的一种方法是使分配器类型成为容器类的模板参数,然后为池分配器的内部堆栈创建一个特殊的分配器类。所以你有这些类型:
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; }
};
为列表类提供一个构造函数可能是一个好主意,该构造函数将为列表用户提供初始化其分配器数据成员的选项(按值)。
你也可以给堆栈一个构造函数,它将把分配器的一个实例传递给它的内部列表的构造函数,原因相同。