删除模板分配的数组,可能已分配索引

时间:2013-07-20 05:13:53

标签: c++ c++11 heap new-operator memory-management

我正在重新编写一个旧的循环缓冲类,我写的更强大。我在堆上分配一个类型为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

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

我可以安全地处理这种情况,或者是否有用户可以做的事情,以便责任不是我的(因为类不在内部分配,用户是)?

编辑* * * 我刚刚意识到在传递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]之后)不幸的是,我不确定是什么导致了这一点。

2 个答案:

答案 0 :(得分:5)

一个想法是通过编写

来部分专门化模板
template<typename T, unsigned int SIZE>
class CircularBuffer<T*> { ... }

以便您可以删除指针。然后,您可以使用enable_if之类的一些SFINAE技巧,因此专门用于指针的模板编译将失败。无论如何,首先删除内存指针从数组指向,然后删除数组本身。这听起来很麻烦。

其他想法是允许用户控制内存的某些部分;您可以从其他容器中复制创意,​​例如通过定义自定义分配器和解除分配器

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构造参数,就像一些容器一样。

答案 1 :(得分: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;

}