有没有办法让未初始化的 类 数组在调用 delete[] 时不会被破坏?
Is there a way to have an array of uninitialized classes that aren't destructed when delete[] is called?
我正在实现一个带有擦除类型的对象池,这样池就可以存储在集合中,也可以存储其他池。该功能已到位,但还存在由于未删除池本身而导致的内存泄漏。我有以下代码:
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;
};
恰好,"Problem!" 是我的问题所在。您可能想知道为什么我将 uinique_ptr 的默认删除替换为不执行任何操作的删除。这是因为在销毁时池中充满了完全虚假的数据,因此当池池被销毁时(或任何携带智能指针的对象池或其他具有析构函数的对象),数组删除将调用每个 class 然后删除导致段错误的虚假智能指针。所以我替换了 noop 析构函数,一切都运行良好。
因此内存泄漏。我已经摆脱了默认的删除,所以每个池对象都离开了它的池。我试过“::operator delete[](arr);”在 "Problem!" 点,因为它应该在不调用对象的析构函数的情况下删除数组,但这会产生 "munmap_chunk(): invalid pointer." 我正在尝试找到一种 C++ 方法来执行此操作,而不需要求助于 malloc 和 free .此外,我想知道是否有一种方法可以首先分配数组,而无需为每个数组成员调用默认构造函数并让它们保持未初始化状态。
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;
};
当然,因为你基本上是 re-inventing 一个 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;
};
我正在实现一个带有擦除类型的对象池,这样池就可以存储在集合中,也可以存储其他池。该功能已到位,但还存在由于未删除池本身而导致的内存泄漏。我有以下代码:
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;
};
恰好,"Problem!" 是我的问题所在。您可能想知道为什么我将 uinique_ptr 的默认删除替换为不执行任何操作的删除。这是因为在销毁时池中充满了完全虚假的数据,因此当池池被销毁时(或任何携带智能指针的对象池或其他具有析构函数的对象),数组删除将调用每个 class 然后删除导致段错误的虚假智能指针。所以我替换了 noop 析构函数,一切都运行良好。
因此内存泄漏。我已经摆脱了默认的删除,所以每个池对象都离开了它的池。我试过“::operator delete[](arr);”在 "Problem!" 点,因为它应该在不调用对象的析构函数的情况下删除数组,但这会产生 "munmap_chunk(): invalid pointer." 我正在尝试找到一种 C++ 方法来执行此操作,而不需要求助于 malloc 和 free .此外,我想知道是否有一种方法可以首先分配数组,而无需为每个数组成员调用默认构造函数并让它们保持未初始化状态。
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;
};
当然,因为你基本上是 re-inventing 一个 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;
};