我正在重新编写一个旧的循环缓冲类,我写的更强大。我在堆上分配一个类型为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]之后)不幸的是,我不确定是什么导致了这一点。
答案 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;
}