我正在实现带有类型擦除的对象池,以便可以将池存储在集合中并可以存储其他池。该功能已到位,但还具有由于不删除池本身而导致的内存泄漏。我有以下代码:
template <typename T>
struct ObjectPool::PoolModel final : PoolConcept {
PoolModel(uint size) : pool( new T[size](), [](T _[]){ /*Problem!*/}) {}
virtual ~PoolModel() {}
private:
std::unique_ptr<T[], std::function<void(T[])>> pool;
};
恰恰是“问题!”是我的问题所在。您可能想知道为什么我将uinique_ptr的默认删除替换为什么都不做的删除。这是因为该池在销毁时充满了伪造的数据,因此当销毁一个池(或任何带有智能指针的对象的池或带有析构函数的其他对象)时,数组delete将调用每个类的析构函数,并且通过删除导致段错误的伪造智能指针来跟进。因此,我替换了noop析构函数,并且所有功能都变了。
因此发生内存泄漏。我已经摆脱了默认的删除操作,因此每个池对象都离开了它的池。我已经尝试过“ :: operator delete [](arr);”在“问题!”点,因为它应该删除一个数组而不调用该对象的析构函数,但是会产生“ munmap_chunk():无效指针”。我正在尝试找到一种执行此操作的C ++方式,而不需要诉诸malloc和free。此外,我想知道是否有一种方法可以首先分配数组,而无需为每个数组成员调用默认构造函数,而只是不对其进行初始化。
答案 0 :(得分:3)
T
的数组必须始终填充有效的T
对象。没有例外。
解决问题的方法是不使用T
对象的数组。相反,应为n个T
对象分配具有适当大小和对齐方式的未初始化存储,然后使用placement-new对其进行初始化。然后,您将负责手动调用T
对象的析构函数。这是解决方案std::vector
,类似的数据结构使用:
template <typename T>
struct ObjectPool::PoolModel final : PoolConcept {
using StorageT = std::aligned_storage_t<sizeof(T), alignof(T)>;
PoolModel(uint capacity)
: pool{ std::make_unique<StorageT[]>(capacity) },
size{0},
capacity{capacity}
{}
virtual ~PoolModel() {
for (size_t i = 0; i < size; ++i) {
T* ptr = std::launder(reinterpret_cast<T*>(&pool[i]));
ptr->~T();
}
}
void insert(T obj) {
assert(capacity > size);
new (&pool[size + 1]) T{std::move(obj)};
++size;
}
private:
std::unique_ptr<StorageT[]> pool;
uint size;
uint capacity;
};
当然,由于您基本上是在重新发明std::vector
,因此可以只使用std::vector
:
template <typename T>
struct ObjectPool::PoolModel final : PoolConcept {
PoolModel(uint capacity) {
pool.reserve(capacity);
}
virtual ~PoolModel() {}
void insert(T obj) {
pool.push_back(std::move(obj));
}
private:
std::vector<T> pool;
};