C++内存管理(3)—重载

应用程序的设计中,我们所说的内存管理就是将系统要处理的内存分配和释放接管过来,内存池是常用的一种设计思路。内存池是在程序的一开始就分配一大块的内存,在后续需要使用内存的地方就直接从内存池中分配出来一块给程序使用,这样就避免了反复的向系统申请和释放内存,从而造成性能上的损失。另一方面,统一的管理还有利于避免内存泄露的出现,因为大量的地方分配内存,容易出现忘记了写delete的情况。要想了解内存管理该怎么做,首先就需要知道C++中给我们提供了哪些东西,我们利用这些东西又可以干什么。
前面两篇中主要讲在系统的各个层面上的内存管理的函数接口,以及它们的使用方式。那些都是系统/Runtime提供给我们的。这一篇中我们就来看看我们可以在这些操作中进行自定义的一些修改。重载是面向对象语言的一个重要的特性。使用重载我们可以在多层继承关系中,让子类能够运行带有自己特色的函数。

1. 调用流程以及可重载部分

首先来看我们在C++中使用内存管理的操作的时候执行的流程,以及这些流程中那些步骤是允许我们去重载的。

重载.png

上图中,我们在app中使用new来创建一个Foo的对象,这个过程在compiler中会解释成右边的形式,也就是调用operator new函数来分配内存,然后调用构造函数,创建对象。operator new又会去调用下一级的::operator new()函数,这个是一个全局的函数。有些地方也将他们叫做操作符,因为他们和+-*/的重载写法一样,叫法不是我们所要去深究的。这里需要注意:
1. operator new和::operator new都是可以重载的;
2. 如果在类中重载了operator new,就会去调用我们类中的operator new, 然后才是调用到全局的::operator new();
3. 我们也可以在类中的operator new或者全局的operator new中不去调用系统提供的接口,这样这个类就没办法在堆中申请内存了;
理解了上述几点,我们就可以看出,实际上我们重载的这些 相当于是hook了一些操作

除了在app中直接使用new,我们还可以使用C++提供给我们的allocater, 这也是STL中容器使用的内存管理工具了。下图找你个就是allocator的调用流程。因为allocator是一个提供的工具,所以它里面是直接使用全局的::operator new的。

分配器.png

从上面的两个图中我们可以看出:

  1. 一旦我们重载了全局的::operator new,那么这个程序中所有的类,以及所有使用new的地方都会走到我们重载的那个全局::operator new中,这个影响是非常大的,也是在程序设计中很少直接这样用的一点。
  2. 在类中重载operator new, 只会影响这个类的操作,所以一般情况下,这种方式是在内存管理中常用的。

2. 例子

首先,我们来重载全局::operator new 和::operator delete,然后看看是不是如我们之前所说的调用流程一致。

#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;
}

验证了全局的重载之后,我们在类中重载operator new和operator delete,看看在类中重载的调用流程是怎样的。

#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;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270