亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++中怎么手動創建一個內存池

發布時間:2021-08-08 12:17:15 來源:億速云 閱讀:414 作者:Leah 欄目:開發技術

今天就跟大家聊聊有關C++中怎么手動創建一個內存池,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

引言

使用new expression為類的多個實例分配動態內存時,cookie導致內存利用率可能不高,此時我們通過實現類的內存池來降低overhead。從不成熟到巧妙優化的內存池,得益于union的分時復用特性,內存利用率得到了提高。

原因

在實例化某個類的對象時(在heap而不是stack中),若不使用array new,則每次實例化時都要調用一次內存分配函數,類的每個實例在內存中都有上下兩個cookie,從而降低了內存的利用率。然而,array new也有先天的缺陷,即只能調用默認無參構造函數,這對于很多沒有提供無參構造函數的類來說是不合適的。

因此,我們可以對于一個沒有實例化的類第一次實例化時,先分配一大塊內存(內存池),這一大塊內存記錄在類中,只有上下兩個cookie,能夠容納多個實例。后續實例化時,若內存池中還有剩余內存,則不必申請內存分配,只在內存池中分配。內存回收時,將實例所占用的內存回收到內存池中。若內存池中無內存,則再申請分配大塊內存。

脫褲子放屁方案

我們以鏈表的形式組織內存池,內存池中每個一個鏈表是一個小桶,這個桶中裝我們實例化的對象。

內存池鏈表的頭結點記錄在類中,即以class staic變量的形式存儲。組織形式如下:

C++中怎么手動創建一個內存池

實現代碼如下:

#include <iostream>
using namespace std;
class DemoClass{
public:
    DemoClass() = default;
    DemoClass(int i):data(i){}
    static void* operator new(size_t size);
    static void operator delete(void *);
    virtual ~DemoClass(){}
private:
    DemoClass *next;
    int data;
    static DemoClass *freeMemHeader;
    static const size_t POOL_SIZE;
};
DemoClass * DemoClass::freeMemHeader = nullptr;
const size_t DemoClass::POOL_SIZE = 24;//設定內存池能容納24個DemoClass對象
void* DemoClass::operator new(size_t size){
    DemoClass* p;
    if(!freeMemHeader){//freeMemHeader為空,內存池中無空間,分配內存
        size_t pool_mem_bytes = size * POOL_SIZE;//內存池的字節大小 = 每個實例的大小(字節數)* 內存池中能容納的最大實例數
        freeMemHeader = reinterpret_cast<DemoClass*>(new char[pool_mem_bytes]);//new char[]分配pool_mem_bytes個字節,因為每個char占用1個字節
        cout << "Info:向操作系統申請了" << pool_mem_bytes << "字節的內存。" << endl;
        for(int i = 0;i < POOL_SIZE - 1; ++i){//將內存池中POOL_SIZE個小塊內存,串起來。
            freeMemHeader[i].next = &freeMemHeader[i + 1];
        }
        freeMemHeader[POOL_SIZE - 1].next = nullptr;
    }
    p = freeMemHeader;//取內存池(鏈表)的頭部,分配給要實例化的對象
    cout << "Info:從內存池中取了" << size << "字節的內存。" << endl;
    freeMemHeader = freeMemHeader -> next;//從內存池中刪去取出的那一小塊地址,即更新內存池
    p -> next = nullptr;
    return p;
}
void DemoClass::operator delete(void* p){
    DemoClass* tmp = (DemoClass*) p;
    tmp -> next = freeMemHeader;
    freeMemHeader = tmp;
}

測試代碼如下:

int main(int argc, char* argv[]){
    cout << "sizeof(DemoClass):" << sizeof(DemoClass) << endl;
    size_t N = 32;
    DemoClass* demos[N];
    for(int i = 0; i < N; ++i){
        demos[i] = new DemoClass(i);
        cout << "address of the ith demo:" << demos[i] << endl;
        cout << endl;
    }
    return 0;
}

其結果如下:

C++中怎么手動創建一個內存池

C++中怎么手動創建一個內存池

可以看到每個DemoClass的實例大小為24字節,內存池一次從操作系統中申請了576個字節的內存,這些內存可以容納24個實例。上面顯示出了每個實例的內存地址,內存池中相鄰實例的內存首地址之差為24,即實例的大小,證明了一個內存池的實例之間確實沒有cookie。

當內存池中內存用完后,又向操作系統申請了576個字節的內存。

由此,只有每個內存池兩側有cookie,而內存池中的實例不存在cookie,相比于每次調用new expression實例化對象都有cookie,內存池的組織形式確實在形式上提高了內存利用率。

那么,有什么問題么?

sizeof(DemoClass)等于24

  1. int data數據域占4個字節

  2. 兩個構造函數一個析構函數各占4字節,共12字節

  3. 額外的指針DemoClass*,在64位機器上,占8個字節

這樣一個DemoClass的大小確實是24字節。wait,what?

我們為了解決cookie帶來的內存浪費,引入了指針next,但卻又引入了8個字節的overhead,脫褲子放屁,多此一舉?

這樣看來確實沒有達到要求,但至少為我們提供了一種思路,不是么?

分時復用改進方案

首先我們先回憶下c++ 中的Union:

在任意時刻,聯合中只能有一個數據成員可以有值。當給聯合中某個成員賦值之后,該聯合中的其它成員就變成未定義狀態了。

結合我們之前不成熟的內存池,我們發現,當內存池中的桶還沒有被分配給實例時,只有next域有用,而當桶被分配給實例后,next域就沒什么用了;當桶被回收時,數據域變無用而next指針又需要用到。這不正是union的特性么?

看一下代碼實現:

#include <iostream>
using namespace std;
class DemoClass{
public:
    DemoClass() = default;
    DemoClass(int i, double p){
        data.num = i;
        data.price = p;
    }
    static void* operator new(size_t size);
    static void operator delete(void *);
    virtual ~DemoClass(){}
private:
    struct DemoData{
        int num;
        double price;
    };
private:
    static DemoClass *freeMemHeader;
    static const size_t POOL_SIZE;
    union {
        DemoClass *next;
        DemoData data;
    };
    
};
DemoClass * DemoClass::freeMemHeader = nullptr;
const size_t DemoClass::POOL_SIZE = 24;//設定內存池能容納24個DemoClass對象
void* DemoClass::operator new(size_t size){
    DemoClass* p;
    if(!freeMemHeader){//freeMemHeader為空,內存池中無空間,分配內存
        size_t pool_mem_bytes = size * POOL_SIZE;//內存池的字節大小 = 每個實例的大小(字節數)* 內存池中能容納的最大實例數
        freeMemHeader = reinterpret_cast<DemoClass*>(new char[pool_mem_bytes]);//new char[]分配pool_mem_bytes個字節,因為每個char占用1個字節
        cout << "Info:向操作系統申請了" << pool_mem_bytes << "字節的內存。" << endl;
        for(int i = 0;i < POOL_SIZE - 1; ++i){//將內存池中POOL_SIZE個小塊內存,串起來。
            freeMemHeader[i].next = &freeMemHeader[i + 1];
        }
        freeMemHeader[POOL_SIZE - 1].next = nullptr;
    }
    p = freeMemHeader;//取內存池(鏈表)的頭部,分配給要實例化的對象
    cout << "Info:從內存池中取了" << size << "字節的內存。" << endl;
    freeMemHeader = freeMemHeader -> next;//從內存池中刪去取出的那一小塊地址,即更新內存池
    p -> next = nullptr;
    return p;
}
void DemoClass::operator delete(void* p){
    DemoClass* tmp = (DemoClass*) p;
    tmp -> next = freeMemHeader;
    freeMemHeader = tmp;
}

對比前一種實現代碼,只是構造函數、數據域和指針域的組織形式發生了變化:

  • 由于數據域增加了price項,構造函數中也增加了對應的參數

  • 數據域被集成定義成一個類自定義struct類型

  • 數據域和指針域被組織為union

測試代碼依舊:

int main(int argc, char* argv[]){
    cout << "sizeof(DemoClass):" << sizeof(DemoClass) << endl;
    size_t N = 32;
    DemoClass* demos[N];
    for(int i = 0; i < N; ++i){
        demos[i] = new DemoClass(i, i * i);
        cout << "address of the " << i << "th demo:" << demos[i] << endl;
        cout << endl;
    }
    return 0;
}

結果:

C++中怎么手動創建一個內存池

C++中怎么手動創建一個內存池

可以看到每個DemoClass的實例大小為24字節,一個內存池的實例之間沒有cookie。

分析一下sizeof(DemoClass)等于24的緣由:

  • data數據域占12個字節(int 4字節、double 8字節)。

  • 兩個構造函數一個析構函數各占4字節,共12字節。

  • 指針DemoClass,在64位機器上,占8個字節,但由于和數據域使用了union,data數據域12個字節中的前8個字節在適當的時機被看作DemoClass,而不占用額外空間,消除了overhead。

這樣一個DemoClass的大小確實是24字節。利用union的分時復用特性,我們消除了初步方案中指針帶來的脫褲子放屁效果。

看完上述內容,你們對C++中怎么手動創建一個內存池有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

c++
AI

高碑店市| 石渠县| 金坛市| 常山县| 彰化市| 高唐县| 永仁县| 八宿县| 建平县| 柳林县| 常山县| 台安县| 页游| 左贡县| 昭通市| 简阳市| 巴青县| 双牌县| 卢湾区| 布尔津县| 钟山县| 德州市| 盐亭县| 农安县| 思南县| 苗栗市| 嫩江县| 桂林市| 安多县| 乌拉特后旗| 开阳县| 神木县| 顺义区| 关岭| 鹤峰县| 遵义县| 长宁县| 府谷县| 荔浦县| 赤壁市| 宿迁市|