简体   繁体   English

C ++模板类循环依赖

[英]C++ Template Class Circular Dependency

During my spare time (as a learning exercise) I've been working on creating some templated containers and allocators in C++, similar to the ones provided as part of the Standard Template Library. 在业余时间(作为学习练习),我一直在努力用C ++创建一些模板化的容器和分配器,类似于标准模板库中提供的那些。

So far the containers I've made are Singly Linked List, Doubly Linked List, Stack, and Queue. 到目前为止,我制作的容器是单链表,双链表,堆栈和队列。 The Stack and Queue both use the Singly Linked List as their internal structure, since I store both the head and tail pointers. 堆栈和队列都使用单链接列表作为其内部结构,因为我存储了头和尾指针。

Now comes my first allocator class, the Pool Allocator. 现在是我的第一个分配器类,池分配器。 Internally it uses one of my Stack objects to Acquire and Release the pre-allocated objects. 在内部,它使用我的Stack对象之一来获取和释放预分配的对象。 I would now like to use this Pool Allocator in conjuction with my Singly Linked List and Doubly Linked List in order to pre-allocate the internal Node objects which store the data. 我现在想将此池分配器与我的单链接列表和双链接列表结合使用,以便预先分配用于存储数据的内部Node对象。 It seems to me like this now creates a circular dependency issue in my project. 在我看来,这现在在我的项目中造成了循环依赖问题。

My normal method of solving dependency issues like this on non-templated classes usually involves forward declarations, pointers, and splitting implementation into a cpp file. 解决非模板类上的此类依存关系问题的常规方法通常涉及前向声明,指针,并将实现拆分为cpp文件。 The problem seems to arise because I can't split template code declarations and implementations into their respective .h and .cpp files. 由于无法将模板代码声明和实现分成各自的.h和.cpp文件,因此似乎出现了问题。

Some code for further reference: 一些代码供进一步参考:

SinglyLinkedList.h: 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: 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: 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
     }
};

I am a bit unsure on the best way to solve this issue. 我不确定解决此问题的最佳方法。 The only way I can think of would be to make the Pool Allocator not use any of my container classes. 我能想到的唯一方法是使Pool Allocator不使用我的任何容器类。 I suppose I could make an internal linked list class that is exclusive to the allocator class, but that seems like unnecessary code duplication. 我想我可以创建一个内部的链表类,该类对分配器类是排他的,但这似乎是不必要的代码重复。

If anyone has any insight on this I would be very glad to hear it. 如果有人对此有任何见解,我将很高兴听到。 I hope I covered everything thoroughly enough and provided an acceptable code example. 我希望我能充分地介绍所有内容并提供可接受的代码示例。 If there is any missing information please let me know. 如果缺少任何信息,请告诉我。 Thanks in advance. 提前致谢。

If I understand you correctly, you have two problems: 如果我对您的理解正确,那么您有两个问题:

  1. You are effectively telling the compiler that a linked list contains a pool allocator, and a pool allocator contains a stack (which contains a linked list), so instantiating any one of those objects would be telling the compiler to allocate an infinitely-recursive set of objects. 您实际上是在告诉编译器一个链表包含一个池分配器,而一个池分配器包含一个堆栈(包含一个链表),因此实例化这些对象中的任何一个都将告诉编译器分配一个无限递归集。对象。

  2. Since your list allocates from your pool allocator and your pool allocator allocates from a list, nothing actually allocates nodes from the free store (eg operators new and delete). 由于列表是从池分配器分配的,而池分配器是从列表分配的,因此实际上没有任何东西从免费存储区中分配节点(例如,运算符new和delete)。

Circular dependencies are bad logic. 循环依赖关系是不好的逻辑。 You need to break one of the dependencies. 您需要打破依赖关系之一。 Since the linked lists' dependencies on your pool allocator is by design, you need to break the other dependency: the fact that pool allocator contains a linked list (in the stack data member) that contains a pool allocator. 由于链表对池分配器的依赖关系是设计使然,因此您需要打破另一个依赖关系:池分配器包含一个链表(在堆栈数据成员中),该链表包含池分配器。 That last part is the root of your problem. 最后一部分是问题的根源。

One way to do this is to make the allocator type a template parameter of your container classes, and then make a special allocator class just for the pool allocator's internal stack. 一种方法是使分配器类型成为容器类的模板参数,然后为池分配器的内部堆栈创建一个特殊的分配器类。 So you'd have these types: 因此,您将拥有以下类型:

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; }

  };

It might be a good idea to give the list classes a constructor that will give the user of the list the option to initialize its allocator data member (by value). 为列表类提供一个构造函数,使列表用户可以选择初始化其分配器数据成员(按值)的选项,可能是一个好主意。

You could also give the stack a constructor that will take and pass an instance of the allocator to its internal list's constructor, for the same reason. 您也可以出于相同的原因,为堆栈提供一个构造函数,该构造函数将采用分配器的实例并将其传递给其内部列表的构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM