2

我正在重新编写一个旧的循环缓冲区类,以使其更健壮。我在 T 类型的堆上分配一个缓冲区(因此该类是模板化的)。但是,我在释放资源时遇到问题,这可能是因为 T 是指向动态分配空间的指针。

简而言之,这是一个具有默认值参数的 ctor

template <typename T, unsigned int SIZE>
CircularBuffer(const T default_val) {
    _buffer = new T[SIZE];
    // assign each block default value, etc
}

// dtor
~CircularBuffer()
{
    delete [] _buffer;
}

但是,例如有人决定这样做:

CircularBuffer<int*, 4> cb(new int); // buffer of 4, holding int*, with default value of new int

// later ~CircularBuffer call is made
// user allocated memory is not freed

我将如何能够(或让用户)释放此内存?我从用户的角度手动尝试过:

delete cb.at(0);  // .at returns T& (so it would effectively return the pointer)
// above is access violation

我试图弄清楚如何在析构函数中执行此操作,但我无法执行任何类型的 delete _buffer[i] 因为编译器认为模板 T 不是指针(即使它可能是)。

我可以安全地处理这种情况,还是用户可以对此做些什么,这样责任就不是我的了(因为类没有在内部分配这个,所以用户是)?

编辑* * * 我刚刚意识到将 T* 作为模板参数传递时使用 new 进行的分配不会返回预期的缓冲区大小。

// call default ctor
CircularBuffer<double*, 2> cb(); // _buffer = new T[SIZE];
// sizeof(_buffer) == 4;
// sizeof(double*) == 4;
// sizeof(double*[2]) == 8;  // _buffer should be this size, as it holds 2 4byte pointers, what happened?

我不确定我是否应该为此提出一个新问题,或者将它与原始问题一起留在这里,但我认为这解释了我之前遇到的一些访问冲突(在尝试取消引用上述实例的 _buffer[1] 之后。不幸的是我不知道是什么原因造成的。

4

2 回答 2

5
template<typename T>
struct is_pointer{
    static const bool value = false;
};

template<typename T>
struct is_pointer<T*>{
    static const bool value = true;
};

template <bool, class T = void> 
struct enable_if 
{};

template <class T> 
struct enable_if<true, T> 
{ 
  typedef T type; 
};

template <bool, class T = void> 
struct disable_if 
{};

template <class T> 
struct disable_if<false, T> 
{ 
  typedef T type; 
};

template <typename T, unsigned int SIZE>
class CircularBuffer
{
public: 
    CircularBuffer(){
        _buffer = new T[SIZE];
    }

    ~CircularBuffer()
    {
        free_helper<T,SIZE>();
        delete [] _buffer;
    }

private:

    template<class U, unsigned int SIZE>
    void free_helper( typename enable_if< is_pointer<U>::value >::type *dummy=0 ){
        //pointer container
        for(int i=0;i<SIZE;++i){
           //delete it?  
           U t = _buffer[i];  
        }
    }

    template<class U, unsigned int SIZE>
    void free_helper( typename disable_if< is_pointer<U>::value >::type *dummy=0 ){
        //none pointer container
    }

    T* _buffer;
};


void main(){

    CircularBuffer<int,10> cb;
    CircularBuffer<int*,10> cb2;

}
于 2013-07-20T06:13:37.720 回答
5

一种想法是通过编写来部分专门化模板

template<typename T, unsigned int SIZE>
class CircularBuffer<T*> { ... }

这样您就可以对可以删除的指针进行操作。然后,您可以使用一些 SFINAE 技巧,例如enable_if,专门用于指针的模板的编译将失败。无论如何,首先删除数组指针指向的内存,然后删除数组本身。这听起来可能有很多麻烦。

另一个想法是让您的用户控制部分内存;您可以从其他容器复制想法,例如通过定义自定义分配器和释放器

template<typename T>
class Buffer {
public:
    typedef function<T(unsigned)> Allocator;
    typedef function<void(T)> Deallocator;

    Buffer(unsigned size,  Allocator allocator, Deallocator deallocator )
        : _size(size), _buffer(new T[1024]), 
          _allocator(allocator), _deallocator(deallocator)
    {
        for(unsigned i=0; i<_size; ++i)
            _buffer[i] = _allocator(i);
    };

    ~Buffer(){
        for(unsigned i=0; i<_size; ++i)
            _deallocator(_buffer[i]);

        delete[] _buffer;
    };

private:
    unsigned _size;
    Allocator _allocator;
    Deallocator _deallocator;
    T* _buffer;
};

...

Buffer<double*> b2(
    128, 
    [](unsigned idx) { return new double(idx + 0.123); }, 
    [](double* d) { cout << "deleting " << *d << endl; delete d; }
);

这是一个非常简单和快速的草稿,但您应该明白这一点。


顺便说一句,使用模板参数作为缓冲区unsigned int SIZE大小SIZE(就像一些容器一样。

于 2013-07-20T05:44:56.190 回答