[英]C++ Template Class Circular Dependency
在业余时间(作为学习练习),我一直在努力用C ++创建一些模板化的容器和分配器,类似于标准模板库中提供的那些。
到目前为止,我制作的容器是单链表,双链表,堆栈和队列。 堆栈和队列都使用单链接列表作为其内部结构,因为我存储了头和尾指针。
现在是我的第一个分配器类,池分配器。 在内部,它使用我的Stack对象之一来获取和释放预分配的对象。 我现在想将此池分配器与我的单链接列表和双链接列表结合使用,以便预先分配用于存储数据的内部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不使用我的任何容器类。 我想我可以创建一个内部的链表类,该类对分配器类是排他的,但这似乎是不必要的代码重复。
如果有人对此有任何见解,我将很高兴听到。 我希望我能充分地介绍所有内容并提供可接受的代码示例。 如果缺少任何信息,请告诉我。 提前致谢。
如果我对您的理解正确,那么您有两个问题:
您实际上是在告诉编译器一个链表包含一个池分配器,而一个池分配器包含一个堆栈(包含一个链表),因此实例化这些对象中的任何一个都将告诉编译器分配一个无限递归集。对象。
由于列表是从池分配器分配的,而池分配器是从列表分配的,因此实际上没有任何东西从免费存储区中分配节点(例如,运算符new和delete)。
循环依赖关系是不好的逻辑。 您需要打破依赖关系之一。 由于链表对池分配器的依赖关系是设计使然,因此您需要打破另一个依赖关系:池分配器包含一个链表(在堆栈数据成员中),该链表包含池分配器。 最后一部分是问题的根源。
一种方法是使分配器类型成为容器类的模板参数,然后为池分配器的内部堆栈创建一个特殊的分配器类。 因此,您将拥有以下类型:
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; }
};
为列表类提供一个构造函数,使列表用户可以选择初始化其分配器数据成员(按值)的选项,可能是一个好主意。
您也可以出于相同的原因,为堆栈提供一个构造函数,该构造函数将采用分配器的实例并将其传递给其内部列表的构造函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.