删除导致调试断言的指针

时间:2015-02-27 02:40:32

标签: c++ pointers memory memory-management

我一直在研究一个内存池分配器类,并且没有出现重大问题,每当我尝试释放由{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ 1}}与new

delete

我正在使用的测试代码:

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

这段代码应该做的是增加存储位置的链接列表,其中存储了类型T的各个数据片段。我认为这是一段有效代码的原因是,因为最初分配的内存被分成大小为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; } 的部分。因此,我认为将内存地址类型转换为类型为sizeof(T)的指针是合适的,这样就可以完全取消分配单个内存块。这将针对整个链表进行,确保释放所有已分配的内存。但是,当我运行代码时,它总是在删除时抛出断言。

单步执行代码表明,在第一次类型转换和T*迭代之后,指针会采用奇怪的值(delete变量,例如:

  

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

之前,它是

  

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

就像它应该的那样)。我已经通过许多不同的方式尝试过这种方法很多次,包括对next进行类型转换而不是void*

这个问题困扰了我好几天,任何帮助都会受到赞赏。谢谢。

2 个答案:

答案 0 :(得分:0)

这不会编译:

struct FreeList
{
    FreeList *next;
};

......但以下内容将会:

struct FreeList
{
    struct FreeList *next;
};

否则,编译器将引发有关不完整的struct定义或未知类型FreeList的错误。

答案 1 :(得分:0)

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

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

你不能在头上调用删除,因为没有通过调用new T()来分配内存;