这看起来应该是显而易见的,但我对它有所了解。我有
class SimpleMemoryPool {
char buffer[10000];
size_t idx;
void *Alloc(size_t nbytes) { idx += nbytes; return &buffer[idx - nbytes]; }
};
inline void* operator new (size_t size, SimpleMemoryPool& pool)
{
return pool.Alloc(size);
}
inline void* operator new[] (size_t size, SimpleMemoryPool& pool)
{
return pool.Alloc(size);
}
我的想法是,我可以从SimpleMemoryPool
中分配新对象,然后在SimpleMemoryPool
被销毁时它们全部被“释放”:
void foo()
{
SimpleMemoryPool pool;
int *arr = new (pool) int[10];
double *arr2 = new (pool) double(3.14);
...do things with arr and arr2...
return; // and arr, arr2 are "released" at this point
}
我已经简化了一个挑剔:上面的代码是粗略的,因为double
不会是8字节对齐的。别担心;我的真实SimpleMemoryPool
代码返回最大对齐块。
接下来你可能会想到下一件事:“谁叫召唤者?!”即,如果我不小心写了
std::string *arr3 = new (pool) std::string;
然后我处于一个受伤的世界,因为编译器会为我生成std::string::string()
的调用,但没有人会调用std::string::~string()
。内存泄漏,随之而来的是坏事。
这是我想要解决的问题。我想要做的基本上是
class SimpleMemoryPool {
...
// (std::enable_if omitted for brevity)
template<typename T, typename... Args>
T *New(Args... args) {
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible!");
void *ptr = this->Alloc(nelem * sizeof (T));
return new (ptr) T(std::forward<Args>(args)...);
}
template<typename ArrayT>
auto NewArray(size_t nelem) -> std::remove_extent<ArrayT>::type {
typedef typename std::remove_extent<ArrayT>::type T;
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible!");
void *ptr = this->Alloc(nelem * sizeof (T));
return new (ptr) T[ nelem ];
}
};
...
int *arr = pool.NewArray<int>(10);
double *arr2 = pool.New<double>(3.14);
std::string *arr3 = pool.New<string>(); // fails the static_assert, hooray!
这种方法的问题在于它很难看。它看起来很糟糕,它邀请后来的维护者来加入并通过添加“适当的”operator new
来“修复”代码,此时我们失去了static_assert
的安全性。
有没有办法充分利用这两个方面 - 通过static_assert进行类型安全,还有一个很好的语法?
您可以假设C ++ 11。我也欢迎C ++ 14答案,即使它们对我没有立即有用。
将成员operator new
添加到我的所有课程(在此示例中为int
和double
)是不可接受的。无论我做什么都必须开箱即用而不需要改变一百万行代码。
这可能是Get type of object being allocated in operator new的副本,但我仍然喜欢为这个特定用例量身定制的答案。可能有一些我不知道的好习语。