1. 程式人生 > >C++ placement new與記憶體池

C++ placement new與記憶體池

有些時候我們需要能夠長時間執行的程式(例如監聽程式,伺服器程式)對於這些7*24執行的程式,我們不應該使用標準庫提供的new 和 delete (malloc和free也算)。這是因為隨著程式的執行,記憶體不斷的被申請和被釋放,頻繁的申請和釋放將會引發記憶體碎片、記憶體不足等問題,影響程式的正常執行。更多的時候核心程式不允許記憶體申請失敗,更不允許異常的出現,因此必須保證每次記憶體申請都是成功的(一般都是核心程式,當然不希望被中斷的後臺程式也是如此)。在這種極端要求下,記憶體池的好處就大大的凸現出來了。

在C++中,可以通過placement new 來實現記憶體池。當然boost也有實現的記憶體池成品(boost::pool),這裡不做過多解釋。

下面是一段使用placement new的程式碼

#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;

void* GlobalBuffer=nullptr;

void InitGlobalBuffer()
{
    GlobalBuffer=malloc(10240);
    cout<<"GlobalBuffer Initialized. Pointer="<<GlobalBuffer<<",size= 10240"<<endl;
}
void DestroyGlobalBuffer()
{
    free(GlobalBuffer);
}

class STK
{
public:
    STK()
    {
        cout<<"In STK::STK(), this="<<this<<endl;
    }
    ~STK()
    {
        cout<<"In STK::~STK(), this="<<this<<endl;
    }
private:
    int a,b,c;
};

int main()
{
    cout<<"Main Start"<<endl;
    InitGlobalBuffer();
    STK* pstk=new (GlobalBuffer) STK;
    pstk->~STK();
    DestroyGlobalBuffer();
    return 0;
}

上面的程式碼中,通過呼叫InitGlobalBuffer()來實現全域性記憶體池的初始化(當然也可以封裝成一個記憶體池類等等...),接下來,

STK* pstk=new (GlobalBuffer) STK;
這行程式碼在已經分配的記憶體(記憶體池)上構造物件。這個構造過程不會因為記憶體不足而丟擲異常或者失敗。(但是建構函式仍可能丟擲異常導致terminate,這一點需要注意)

構造物件成功之後就可以正常使用此物件了。

需要注意的是,物件使用完畢之後需要手動進行解構函式的呼叫,如下

pstk->~STK();
這一步將呼叫STK類的解構函式STK::~STK(),但是記憶體並不會被釋放。

最後通過DestroyGlobalBuffer()實現對全域性記憶體池的釋放。

記憶體池技術能夠顯著的減少記憶體申請時間,完全避免了記憶體碎片、記憶體不足等問題。與記憶體池相關的技術還有很多,在此就不詳細敘述了。