有没有一种方法可以使未初始化的类数组在调用delete []时不会被破坏?

时间:2019-06-12 03:39:10

标签: c++ memory-leaks destructor delete-operator type-erasure

我正在实现带有类型擦除的对象池,以便可以将池存储在集合中并可以存储其他池。该功能已到位,但还具有由于不删除池本身而导致的内存泄漏。我有以下代码:

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。此外,我想知道是否有一种方法可以首先分配数组,而无需为每个数组成员调用默认构造函数,而只是不对其进行初始化。

1 个答案:

答案 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;
};