我为池分配器编写了一个类,并使用一些基本结构sf::Texture
对其进行了测试,它似乎正常工作。但是,当我使用它将sf::Font
复制到已分配的块时,我收到以下错误:
Exception thrown at 0x0F19009E (sfml-graphics-d-2.dll) in ChernoGame.exe: 0xC0000005: Access violation writing location 0xCDCDCDCD.
抛出此错误的代码:
ResourceType *newResource = (ResourceType*)allocator.alloc();
ResourceType t;
*newResource = t; //error is thrown from here
当我为sf::Texture
使用它时,它正常工作,只有在我使用sf::Font
对于sf :: Font,类的大小是76个字节,对齐是4个字节,我的分配器跟着它并分配一个76字节的块,4字节对齐,我无法弄清楚如何解决这个问题错误。
修改:我为sf::SoundBuffer
尝试了它,并且它会抛出类似的错误。
Pool Allocator初始化:
bool PoolAllocator::init(const unsigned int & numBlocks, const unsigned int & blockSize, const int&alignment)
{
if (mpMemoryBlock)
{
return false;
}
// assert(alignment & (alignment - 1) == 0);
auto expandedBlockSize = alignUp(blockSize, alignment);
mBlockSize = expandedBlockSize;
mAlignment = alignment;
mBlocks = numBlocks;
mpMemoryBlock = malloc((expandedBlockSize * numBlocks) + alignment);
if (!mpMemoryBlock)
{
return false;
}
auto currentBlock = alignUp((uintptr_t)mpMemoryBlock, alignment);
nextFreeBlock = currentBlock;
auto nextBlock = currentBlock;
mpActualBlock = (void*)currentBlock;
for (int i = 0; i < static_cast<int>(numBlocks); i++)
{
nextBlock = currentBlock + expandedBlockSize;
auto alignedForNextPointerStorage = alignUp(currentBlock, sizeof(uintptr_t));
*((uintptr_t*)alignedForNextPointerStorage) = nextBlock;
currentBlock = nextBlock;
}
auto alignedForNextPointerStorage = alignUp(currentBlock, sizeof(uintptr_t));
*((uintptr_t*)alignedForNextPointerStorage) = 0;
return true;
}
池分配器分配:
void * PoolAllocator::alloc()
{
if (*((uintptr_t*)nextFreeBlock) == 0)
{
return nullptr;
}
void *result = (void*)nextFreeBlock;
nextFreeBlock = *((uintptr_t*)alignUp(nextFreeBlock, sizeof(uintptr_t)));
return result;
}
Pool Allocator deallocation:
void PoolAllocator::dealloc(void* address)
{
auto nextPointer = alignUp((uintptr_t)address, sizeof(uintptr_t));
if ((alignUp((uintptr_t)address, mAlignment) == (uintptr_t)address) && (mpActualBlock <= address) && !((uintptr_t)address >= ((uintptr_t)mpActualBlock + (mBlocks * mBlockSize))))
{
*(uintptr_t*)nextPointer = nextFreeBlock;
nextFreeBlock = nextPointer;
}
else
{
throw std::runtime_error("Illegal deallocation of memory address : " + (uintptr_t)address);
}
}
答案 0 :(得分:1)
根据你的评论,你只是通过深度复制整个对象来“初始化”这些新的对象实例。
虽然这对于C结构可以正常工作,但它有一个倾向于打破非平凡的C ++类,这些类将执行自己的内存分配。
想象一下以下两个简单的类:
class A {
int number = 5;
};
class B {
int *number;
B() : number(new int()) { *number = 5; }
~B() { delete number; }
}
现在假设你创建了一个类A
的对象并复制它:
A a1;
A *a2 = reinterpret_cast<A*>(new char[sizeof(A)]);
memcpy(a2, &a1, sizeof(A));
你最终会得到两个类A
的对象。
现在让我们用B
重复一遍:
B b1;
B *b2 = reinterpret_cast<A*>(new char[sizeof(B)]);
memcpy(b2, &b1, sizeof(B));
似乎也喜欢这项工作?确实如此。但是,一旦任何一个对象被直接销毁(或超出范围),另一个对象也会破坏,导致访问冲突。为什么?让我们用范围扩展上面的例子:
B *b2 = reinterpret_cast<A*>(new char[sizeof(B)]);
{
B b1;
memcpy(b2, &b1, sizeof(B));
}
因此,在运行此代码之后,您假设b2
指向类B
的有效实例。它仍然存在 - 内存已分配 - 但b2
内的指针不再有效。
b1
时,其构造函数将为整数分配空间并存储该指针(number
)。b2
。b1
超出范围,析构函数释放分配的整数。b2
仍然指向已由b1
解除分配的前一个整数。作为进一步说明,你正在做什么 - 创建一个新对象并将其复制到池分配的内存 - 听起来是个好主意但最终它会破坏池分配器/内存管理背后的整个目的:你想要的以避免首先重新分配对象。
值得庆幸的是,C ++内置了一个用于此目的的机制,称为 placement new 。利用这个,您应该可以执行以下操作:
#include <new>
// Other code here ...
sf::Font* myFont = new(fontAllocator.alloc()) sf::Font;
// Later to destroy the font:
myFont->~Font();
fontAllocator.dealloc(myFont);