C++內(nèi)存管理(3)—重載

應(yīng)用程序的設(shè)計(jì)中,我們所說(shuō)的內(nèi)存管理就是將系統(tǒng)要處理的內(nèi)存分配和釋放接管過(guò)來(lái),內(nèi)存池是常用的一種設(shè)計(jì)思路。內(nèi)存池是在程序的一開(kāi)始就分配一大塊的內(nèi)存,在后續(xù)需要使用內(nèi)存的地方就直接從內(nèi)存池中分配出來(lái)一塊給程序使用,這樣就避免了反復(fù)的向系統(tǒng)申請(qǐng)和釋放內(nèi)存,從而造成性能上的損失。另一方面,統(tǒng)一的管理還有利于避免內(nèi)存泄露的出現(xiàn),因?yàn)榇罅康牡胤椒峙鋬?nèi)存,容易出現(xiàn)忘記了寫(xiě)delete的情況。要想了解內(nèi)存管理該怎么做,首先就需要知道C++中給我們提供了哪些東西,我們利用這些東西又可以干什么。
前面兩篇中主要講在系統(tǒng)的各個(gè)層面上的內(nèi)存管理的函數(shù)接口,以及它們的使用方式。那些都是系統(tǒng)/Runtime提供給我們的。這一篇中我們就來(lái)看看我們可以在這些操作中進(jìn)行自定義的一些修改。重載是面向?qū)ο笳Z(yǔ)言的一個(gè)重要的特性。使用重載我們可以在多層繼承關(guān)系中,讓子類能夠運(yùn)行帶有自己特色的函數(shù)。

1. 調(diào)用流程以及可重載部分

首先來(lái)看我們?cè)贑++中使用內(nèi)存管理的操作的時(shí)候執(zhí)行的流程,以及這些流程中那些步驟是允許我們?nèi)ブ剌d的。

重載.png

上圖中,我們?cè)赼pp中使用new來(lái)創(chuàng)建一個(gè)Foo的對(duì)象,這個(gè)過(guò)程在compiler中會(huì)解釋成右邊的形式,也就是調(diào)用operator new函數(shù)來(lái)分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù),創(chuàng)建對(duì)象。operator new又會(huì)去調(diào)用下一級(jí)的::operator new()函數(shù),這個(gè)是一個(gè)全局的函數(shù)。有些地方也將他們叫做操作符,因?yàn)樗麄兒?-*/的重載寫(xiě)法一樣,叫法不是我們所要去深究的。這里需要注意:
1. operator new和::operator new都是可以重載的;
2. 如果在類中重載了operator new,就會(huì)去調(diào)用我們類中的operator new, 然后才是調(diào)用到全局的::operator new();
3. 我們也可以在類中的operator new或者全局的operator new中不去調(diào)用系統(tǒng)提供的接口,這樣這個(gè)類就沒(méi)辦法在堆中申請(qǐng)內(nèi)存了;
理解了上述幾點(diǎn),我們就可以看出,實(shí)際上我們重載的這些 相當(dāng)于是hook了一些操作

除了在app中直接使用new,我們還可以使用C++提供給我們的allocater, 這也是STL中容器使用的內(nèi)存管理工具了。下圖找你個(gè)就是allocator的調(diào)用流程。因?yàn)閍llocator是一個(gè)提供的工具,所以它里面是直接使用全局的::operator new的。

分配器.png

從上面的兩個(gè)圖中我們可以看出:

  1. 一旦我們重載了全局的::operator new,那么這個(gè)程序中所有的類,以及所有使用new的地方都會(huì)走到我們重載的那個(gè)全局::operator new中,這個(gè)影響是非常大的,也是在程序設(shè)計(jì)中很少直接這樣用的一點(diǎn)。
  2. 在類中重載operator new, 只會(huì)影響這個(gè)類的操作,所以一般情況下,這種方式是在內(nèi)存管理中常用的。

2. 例子

首先,我們來(lái)重載全局::operator new 和::operator delete,然后看看是不是如我們之前所說(shuō)的調(diào)用流程一致。

#include <iostream>

using namespace std;

void* myAlloc(size_t size)
{
    return malloc(size);
}

void myFree(void* ptr)
{
    return free(ptr);
}

inline void* operator new(size_t size) 
{
    cout << " global new() " << endl;
    return myAlloc(size);
}

inline void* operator new[](size_t size)
{
    cout << " global new[]() " << endl;
    return myAlloc(size);
}

inline void operator delete(void* ptr)
{
    cout << " global delete() " << endl;
    return myFree(ptr);
}

inline void operator delete[](void* ptr)
{
    cout << " global delete[]() " << endl;
    return myFree(ptr);
}

int main()
{
    int *pA = new int(10);
    delete pA;

    int* pArr = new int[20];
    delete[] pArr;

    return 0;
}

驗(yàn)證了全局的重載之后,我們?cè)陬愔兄剌doperator new和operator delete,看看在類中重載的調(diào)用流程是怎樣的。

#include <iostream>

using namespace std;

void* myAlloc(size_t size)
{
    return malloc(size);
}

void myFree(void* ptr)
{
    return free(ptr);
}

// 全局重載new()
inline void* operator new(size_t size) 
{
    cout << " global new() " << endl;
    return myAlloc(size);
}

// 全局重載new[]
inline void* operator new[](size_t size)
{
    cout << " global new[]() " << endl;
    return myAlloc(size);
}

// 全局重載delete
inline void operator delete(void* ptr)
{
    cout << " global delete() " << endl;
    return myFree(ptr);
}

// 全局重載delete[]
inline void operator delete[](void* ptr)
{
    cout << " global delete[]() " << endl;
    return myFree(ptr);
}


class Foo
{
public:
    Foo() {
        cout << "foo construct" << endl;
    }
    ~Foo() {
        cout << "foo deconstruct" << endl;
    }

    // 類中重載 new
    void* operator new(size_t size)
    {
        cout << "class member new" << endl;
        return ::operator new(size);
    }

    // 類中重載 delete
    void operator delete(void* ptr)
    {
        cout << "class member delete" << endl;
        return ::operator delete(ptr);
    }

    // 類中重載new[]
    void* operator new[](size_t size)
    {
        cout << "class member new[]" << endl;
        return ::operator new[](size);
    }

    // 類中重載 delete[]
    void operator delete[](void* ptr)
    {
        cout << "class member delete[]" << endl;
        return ::operator delete[](ptr);
    }
};


int main()
{
    //int *pA = new int(10);
    //delete pA;

    //int* pArr = new int[20];
    //delete[] pArr;

    //Foo *foo = new Foo;
    //delete foo;

    Foo *foo = new Foo[10];
    delete[] foo;

    return 0;
}
  1. 在類中重載operator new[]和operator delete[]
  2. 重載new() 和 delete()
class Screen {
public:
    Screen(int x) :i(x) {};
    int geti() { return i; };

    void* operator new(size_t size) {
        Screen *p;
        if (!freeStore) {
            size_t chunk = screenChunk * size;
            freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
            for (; p != &freeStore[screenChunk - 1]; ++p) {
                p->next = p + 1;
            }
            p->next = 0;
        }
        p = freeStore;
        freeStore = freeStore->next;
        return p;
    }

    void operator delete(void* ptr, size_t) {
        (static_cast<Screen*>(ptr))->next = freeStore;
        freeStore = static_cast<Screen*>(ptr);
    }

private:
    Screen* next;
    static Screen* freeStore;
    static const int screenChunk;
    int i;
};

Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容