2011-12-21 123 views
1

我在寫一個簡單的內存競技場分配器,並面臨一個異常安全的小問題。情況是當你分配一個本身調用分配器的對象時。內存池的目標是一次分配一堆對象,然後在池被銷燬時全部刪除它們。內存競技場的異常安全

{ 
    MemoryArena m; 
    std::string* ptr = m.Allocate<std::string>(); 
    // use ptr whatever 
    // Cleaned up when pool is destroyed 
} 

但是,當它被多次使用時,這會變得相當討厭。如果清理內部分配,那麼之後可以使用它 - 這不是一個錯誤的假設,因爲它的定義是永遠不會刪除對象,直到它的生命週期結束。考慮:

struct X { 
    X(MemoryArena* ptr, std::string*& ref) { 
     ref = ptr->Allocate<std::string>(); 
     throw std::runtime_error("hai"); 
    } 
}; 
MemoryArena m; 
std::string* ptr; 
m.Allocate<X>(&m, ptr); 
// ptr is invalid- even though it came from the arena 
// which hasn't yet been destroyed 

但如果內部分配清理,外部也分配不能清理,因爲內存分配競技場線性他們就像在一個硬件堆棧,所以我出現內存泄漏。所以要麼我通過早期銷燬對象來破壞我的語義,要麼泄漏內存。

有關如何解決此問題的任何建議?

+2

什麼是'MemoryArena'?我們怎麼能不知道它說什麼? – Nawaz 2011-12-21 15:19:47

+0

目前還不清楚你如何泄漏記憶,何時在競技場被釋放時它將被釋放。 – zvrba 2011-12-21 15:27:11

+0

@zvrba:它被分配,但不可用,因爲對象從來沒有被構造過,並且指針從不返回,所以內存泄漏直到該區域被銷燬。 – Puppy 2011-12-21 15:27:49

回答

2

也許這只是適用的示例代碼,但我不認爲用戶應該認爲ptrX的構造函數拋出時是有效的。它也可能在裁判被分配之前拋出。

所以我會說清理內部對象,如果你可以(即如果競技場的前面目前位於內部對象的末尾)。好的,競技場的分配變得無效,這是不正常的,但是這是一個分配,無論如何都不應該將它分配到現實世界中。

也許你可以用「軟」分配的概念來明確這一點。不能保證永遠活着,因爲雖然它仍然是「軟」,但它可以被釋放回競技場。那麼X的構造函數會做這樣的事情:

SoftPtr<std::string> tmp(ptr->SoftAllocate<std::string>()); 
stuff_that_might_throw(); 
ref = tmp.release(); 

執行的SoftPtr析構函數沒有不先調用release意味着對對象的引用已經暴露。它調用MemoryArena的函數,它是這樣的:

  • 銷燬對象
  • 檢查這是否是從舞臺
    • 如果是最新的分配,從舞臺上的電流減去大小位置指針
    • 如果沒有,別的什麼也不做(內存泄漏)

因此,任何數量的alloc可以被「退出」,只要它以相反的順序完成。

+0

優秀的建議。我可能會讓指針變得無效。 – Puppy 2011-12-21 21:23:29

0

通過內存池的語義,並且如您在問題中所述,只有池可以被釋放,而不是單個對象。然而你想要做到這一點。

一種可能性是配備分配器brk-like functions來獲取和設置下一個分配地址。這給你一個底層的機制,你可以用它來構建任何你想要的東西。