如何将sf :: Font复制到自定义池分配的内存中?

时间:2017-01-29 08:36:48

标签: c++ memory-management sfml dynamic-memory-allocation

我为池分配器编写了一个类,并使用一些基本结构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);
    }

}

1 个答案:

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