删除导致调试断言的指针

Deleting Pointer Causing Debug Assert

我一直在研究内存池分配器 class,没有出现重大问题,没有 Visual Studio 的调试断言 (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse) ) 每当我试图用 delete.

释放由 new 分配的内存时被抛出
typedef uintptr_t   uptr;
typedef uint8_t     u8;
typedef uint16_t    u16;
typedef uint32_t    u32;
typedef uint64_t    u64;
typedef int8_t      s8;
typedef int16_t     s16;
typedef int32_t     s32;
typedef int64_t     s64;

struct FreeList
{
    FreeList *next;
};

template<class T, u8 alignment, u32 poolSize>
class PoolAllocator
{
private:
    u8          _paddedSize;    // The size in bytes of each allocated chunk of memory. 
    u32         _numAllocations;// The number of allocations made by the pool.          
    u32         _freeBytes;     // The number of bytes left in the pool.                
    u32         _usedBytes;     // The number of bytes currently occupied in the pool.  
    FreeList*   _freeListHead;  // A pointer to a freed space in memory.                
public:
    PoolAllocator() :
        _paddedSize((sizeof(T) > sizeof(uptr) ? sizeof(T) : sizeof(uptr))),
        _numAllocations(0),
        _freeBytes(0),
        _usedBytes(0),
        _freeListHead(nullptr)
    {
        _freeListHead = reinterpret_cast<FreeList*>(operator new (_paddedSize * poolSize));
        _freeBytes = _paddedSize * poolSize;

        uptr current = reinterpret_cast<uptr>(_freeListHead);
        uptr last = current + (_paddedSize * poolSize);
        for (int i = 0; i < poolSize-1; i++)
        {
            uptr next = current + _paddedSize;
            (reinterpret_cast<FreeList*>(current))->next = reinterpret_cast<FreeList*>(next);
            current += _paddedSize;
        }
        reinterpret_cast<FreeList*>(current)->next = nullptr;
    }
    T *allocate()
    {
        if (_freeListHead != nullptr && _freeBytes >= _paddedSize)  // Make sure the pool has memory left
        {
            uptr *toReturn = reinterpret_cast<uptr*>(_freeListHead);    // Cast the pointer to a modifiable data type.
            _freeListHead = _freeListHead->next;    // VITAL THAT THIS IS BEFORE SETTING DATA TO 0.
            *toReturn = 0;  // Set the data at the memory location to 0.

            _freeBytes -= _paddedSize;
            _usedBytes += _paddedSize;
            _numAllocations++;

            printf("Allocated %d bytes of memory at %p.\n", _paddedSize, toReturn);
            return reinterpret_cast<T*>(toReturn);
        }
        else
        {
            printf("Pool allocator out of memory! Returning nullptr.\n");
            return nullptr;
        }
    }
    void free(T **ptr)
    {
        FreeList *newHead = reinterpret_cast<FreeList*>(*ptr);
        *ptr = nullptr;
        newHead->next = _freeListHead;
        _freeListHead = newHead;

        _freeBytes += _paddedSize;
        _usedBytes -= _paddedSize;
        _numAllocations--;
        printf("Freed %d bytes of memory at %p.\n", _paddedSize, _freeListHead);
    }
    void clear()
    {
        assert(_usedBytes == 0);

        FreeList *head = _freeListHead;
        while (head != 0)
        {
            FreeList *next = head->next;
            delete reinterpret_cast<T*>(head);
            head = next;
        }

        _paddedSize = 0;
        _numAllocations = 0;
        _freeBytes = 0;
        _usedBytes = 0;
        _freeListHead = nullptr;
    }
};

我使用的测试代码:

int main()
{
    PoolAllocator<int, 4, 4> pool;
    int *a, *b, *c, *d, *e;

    a = pool.allocate();
    b = pool.allocate();
    c = pool.allocate();
    d = pool.allocate();
    pool.free(&a);
    e = pool.allocate();

    printf("A | %p\t%d\nB | %p\t%d\nC | %p\t%d\nD | %p\t%d\nE | %p\t%d\n", a, 0, b, *b, c, *c, d, *d, e, *e);

    pool.free(&b);
    pool.free(&c);
    pool.free(&d);
    pool.free(&e);
    pool.clear();

    return 0;
}

问题所在:

void clear()
{
    assert(_usedBytes == 0);

    FreeList *head = _freeListHead;
    while (head != 0)
    {
        FreeList *next = head->next;
        delete reinterpret_cast<T*>(head); // Debug assert
        head = next;
    }

    _paddedSize = 0;
    _numAllocations = 0;
    _freeBytes = 0;
    _usedBytes = 0;
    _freeListHead = nullptr;
}

这段代码应该做的是通过内存位置的链接列表递增,其中存储了类型 T 的各个数据片段。我认为这是一段有效代码的原因是,因为最初分配的内存被分成 sizeof(T) 大小的片段。因此,我假设将内存地址类型转换为 T* 类型的指针是合适的,这样单个内存块将被完全释放。这将对整个链表完成,确保释放所有分配的内存。但是,当我 运行 代码时,它总是在删除时抛出断言。

单步执行代码显示,在第一次迭代类型转换和 deleteing 之后,指针呈现奇怪的值(next 变量,例如:

next 0x004b2864 {next=0xfeeefeee {next=??? } } FreeList *

而以前是

next 0x006c2864 {next=0x006c2860 {next=0x006c285c {next=0x00000000 } } } FreeList *

正如它应该的那样)。我以多种不同的方式多次尝试过这种方法,包括将类型转换为 void* 而不是 T*.

这个问题已经困扰我好几天了,如有任何帮助,我们将不胜感激。谢谢。

这不会编译:

struct FreeList
{
    FreeList *next;
};

...但以下将:

struct FreeList
{
    struct FreeList *next;
};

否则,编译器会抛出结构定义不完整或类型未知的错误FreeList

reinterpret_cast<T*>(head)->~T();

我测试了这个并且不再通过替换 delete reinterpret_cast(head);

得到断言

你不能调用 delete on head 因为内存不是通过调用 new T() 分配的;