我一直在研究一个内存池分配器类,并且没有出现重大问题,每当我尝试释放由{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ 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*
。
这个问题困扰了我好几天,任何帮助都会受到赞赏。谢谢。
答案 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()来分配内存;