2017-01-29 54 views
-3

我寫了一個池分配器的類,並用一些基本結構對它進行了測試,它似乎能夠正常工作。然而,當我用它來一個sf::Font複製到分配的內存塊,我得到了以下錯誤:如何將sf :: Font複製到自定義池分配的內存中?

Exception thrown at 0x0F19009E (sfml-graphics-d-2.dll) in ChernoGame.exe: 0xC0000005: Access violation writing location 0xCDCDCDCD. 

拋出此錯誤代碼:當我用這個sf::Texture它工作正常

ResourceType *newResource = (ResourceType*)allocator.alloc(); 
ResourceType t; 
*newResource = t; //error is thrown from here 

,這個錯誤是隻有當我使用sf::Font

對於sf :: Font類的大小是76字節和對齊是4字節,我的分配器是跟隨此並分配一個76字節塊與4字節對齊,我可以不知道如何解決這個錯誤。

編輯:我試過它爲sf::SoundBuffer它是拋出類似的錯誤。

池分配器初始化:

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

池分配器釋放:

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

你能不能展示你的課? –

+0

你如何實際初始化這些實例?在我看來,就像你在跳過SFF的'sf :: Font'的構造函數一樣,只是施展你的記憶。許多SFML構造函數都是不重要的,因此您不能跳過它們。 – Mario

+0

@Mario這就是爲什麼我創建ResourceType t並將其複製到分配的塊中,應該將默認構造對象t複製到newResource中的原因? –

回答

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

,你認爲b2B類的有效實例。它仍然會 - 分配內存 - 但b2內的指針不再有效。

  • 當您創建b1,它的構造會爲一個整數分配空間和存儲指針(number)。
  • 此指針也被複制到b2
  • 現在b1超出範圍,析構函數釋放分配的整數。
  • 現在b2仍指向已由b1重新分配的前一個整數。
  • 如果您現在嘗試訪問整數,則會爆炸。

作爲進一步的說明,你在做什麼 - 創建一個新對象,並將其複製到池分配的內存 - 聽起來像一個好主意,但最終它擊敗池分配器背後的整個目的/內存管理:您希望避免首先重新分配對象。


值得慶幸的是有內置到C++正是這種目的的技工,這就是所謂放置新。利用這一點,你應該能夠做到以下幾點:

#include <new> 

// Other code here ... 

sf::Font* myFont = new(fontAllocator.alloc()) sf::Font; 

// Later to destroy the font: 
myFont->~Font(); 
fontAllocator.dealloc(myFont); 
+0

非常感謝,很好的解釋,關於你提出的替代方案,'placement new'與new的原因相比有相似的開銷,它會再次破壞池分配器的目的爲我想避免由於'new'造成的開銷* –

+0

@KaranJoisher在調用* new *或* placement new *時沒有任何開銷。 「問題」是**內存碎片**。如果您想了解更多信息,請閱讀該主題。 – Mario

+1

@KaranJoisher短版:'new'必須首先找到正確大小的連續內存,這會使程序的地址空間越碎片越難。放置新的(使用池分配器)可以讓您跳過這個,這是性能增益/原因。 – Mario

相關問題