2014-04-08 17 views
4

我想借的boost::fast_pool_allocator以下通告功能的優勢(見the Boost documentation for Boost Pool):如何防止在由boost :: fast_pool_allocator管理的對象上調用析構函數?

例如,你可以有一個情況下,你要在一個點分配 一堆小物件,和然後到達您的 程序中的一個點,這些程序不再需要它們。使用游泳池接口, 您可以選擇運行自己的析構函數或只是把它們關進 遺忘 ...

(此報價見here。)

關鍵短語拖放關閉被遺忘。我不要想要調用這些對象的析構函數。

(原因是我有數以百萬計的小物件在堆上形成一個非常複雜的所有權網絡,當單個最父物件熄滅時,我的程序需要20分鐘才能調用所有的析構函數堆棧。我不需要這些析構函數調用,因爲沒有希望的副作用,所有的記憶都包含在boost::pool內。)

不幸的是,儘管上述文件的承諾,以及boost::pool概念的承諾,我無法找到防止被調用的被管理對象的析構函數的方法。

的問題很容易在一個小樣本程序分離:

class Obj 
{ 
public: 
    ~Obj() 
    { 
     // Placing a breakpoint here indicates that this is *always* reached 
     // (except for the crash scenario discussed below) 
     int m = 0; 
    } 
}; 

typedef std::map<int, Obj, std::less<int>, 
       boost::fast_pool_allocator<std::pair<int const, Obj>>> 
     fast_int_to_int_map; 

class Foo 
{ 
public: 
    ~Foo() 
    { 
     // When the following line is uncommented, the program CRASHES 
     // when the destructor is exited - because the Obj destructors 
     // are called on the invalid Obj ghost instances 

     //boost::singleton_pool<boost::fast_pool_allocator_tag, 
     //     sizeof(std::pair<int const, Obj>)>::purge_memory(); 
    } 

    fast_int_to_int_map mmap; 
}; 

void mfoo() 
{ 
    // When this function exits, the Foo instance goes off the stack 
    // and its destructor is called, in turn calling the destructors 
    // of the Obj instances - this is NOT desired! 

    Foo foo; 
    foo.mmap[0] = Obj(); 
    foo.mmap[1] = Obj(); 
} 

int main() 
{ 
    mfoo(); 

    // The following line deallocates the memory of the pool just fine - 
    // but does nothing to prevent the destructors of the Obj instances 
    // from being called 

    boost::singleton_pool<boost::fast_pool_allocator_tag, 
          sizeof(std::pair<int const, Obj>)>::purge_memory(); 
} 

如在代碼註釋所指出的,它們由boost::pool管理的Obj實例的析構函數總是調用。

我能做些什麼來使Boost Pool文檔drop them off into oblivion有希望的引用成真?

回答

1

默認情況下池使用default_user_allocator_new_delete分配器。這將通過首先調用析構函數然後回收底層內存來銷燬底層對象。 default_user_allocator_malloc_free將導致malloc'ed內存不扣除析構函數 - 因此drop[ing] them off into oblivion回收。也就是說,如果你的樹真的很複雜,那麼使用free而不是發射析構函數似乎是一個非常好的方法,可以開始從下面切出分支並潛在地開始泄漏你無法覆蓋的內存。

+0

有一個頂級對象,並且每個作爲容器的數據成員都通過所有嵌套對象使用自定義分配器等,因此沒有通過樹的層次結構丟失的單個路徑。然後,我簡單地讓單個頂層對象從堆棧中移出,並且*它的析構函數在所有內存池中調用'purge'。非常感謝這個答案 - 回想起來很明顯!當然,這是'malloc/free'和'new/delete'之間的巨大區別 - 前者不會調用構造函數和析構函數。我從不使用'malloc/free',所以沒有想到它。 –

+0

我正在嘗試這個 - 你知道'boost :: default_user_allocator_malloc_free'分配器*將確保**構造函數**被調用嗎? –

+0

我不知道 - 在你的例子中你明確地調用了構造函數。我認爲你會在你的實施中做類似的事情。 – Andy

3

您將自定義分配器傳遞到您的std::map類中。因此,這個分配器將用於std::map的所有內容:對於所有的Obj數據以及對於std::map的二叉樹的節點。因此,如果在Foo的析構函數中調用purge_memory(),那麼所有這些內存都將無效,並且會在std::map析構函數中崩潰。

您認爲自定義分配器負責對象的取消分配是不正確的:這是一個std::map的任務來釋放所有對象。因此,無論您是通過自定義分配器還是使用默認分配器,都將調用~Obj()

我沒有看到任何優雅的解決方案爲您的問題。但是,這種方法應該工作:

  • 使用placement new創建一個從池的內存Foo對象,
  • 使用此Foo對象像往常一樣,
  • 呼叫purge_memory()從池中釋放所有內存。沒有破壞者~Obj~std::map將被調用。
+0

謝謝!我得出了同樣的結論 - 關於這個事實,即自定義分配器負責內存的定位,而不是調用構造函數或析構函數。現在我清楚這些是兩個完全獨立的過程。 I.e.(1)使用系統SDK內存調用,'new' /'delete'或'malloc' /'free'分配/釋放內存本身;和(2)呼叫Ctor和Dtor's。自定義分配器僅**對前者(#1)負責,而不對後者負責。 –

+0

然而,我的混淆的一個主要部分來自這樣一個事實,即在上面的代碼中,函數purge_memory()不做任何事情**。如果它是**'set' **,而不是'map','purge_memory'會釋放內存。然而,當'map'(內部)在自定義分配器上調用'new'時,'map'創建的內部對象的**大小**不是**的大小。這就是爲什麼'purge_memory()'什麼都不做,在上面。 (在'set'的情況下,內部'new'請求**的大小是包含對象的大小,所以它在這種情況下起作用。)我現在正在處理後一個問題。 –

0

此問題的答案載於@ qehgt答案下的評論中,詳細請見this new question

即:

有對象實例化的兩個相關方面和缺失之間的清楚和正式區分:

  • (1)的存儲器分配和解除分配

  • (2)調用構造函數和析構函數

自定義分配器的用途僅限於(1)。

容器對象處理(2),它們調用自定義分配器中的函數來確定構建對象的內存位置,並告訴自定義分配器可以釋放給定對象的分配內存。 但是對構造函數/析構函數本身的調用是由容器而不是由自定義分配器完成的。

因此,要實現這一問題的目標,方式如下:通過new聲明容器對象,永遠不會調用它delete(但使用自定義的分配器,以保證你以後在容器中創建對象存儲在存儲池,然後手動調用purge_memory())自己釋放內存池:

class Obj { // has operator<() for use with std::set }; 

typedef std::set<Obj, std::less<Obj>, boost::fast_pool_allocator<Obj>> fast_set_obj; 

// Deliberately do not use a managed pointer - 
// I will *NOT* delete this object, but instead 
// I will manage the memory using the memory pool!!! 
fast_set_obj * mset = new fast_set_obj; 

// ... add some Obj's to 'mset' 
mset->insert(Obj()); 
mset->insert(Obj()); 

// Do something desireable with the set ... 
... 

// All done. 
// It's time to release the memory, but do NOT call any Obj destructors. 
// The following line of code works exactly as intended. 

boost::singleton_pool<boost::fast_pool_allocator_tag, sizeof(Obj const)>::purge_memory(); 

(上面的代碼從鏈接的問題採取以上)

然而,我仍然有一個問題,並不直接相關到當前問題背後的意圖:如果使用map而不是set,則上述代碼不起作用。詳情請參閱linked question

相關問題