C++内存管理-重载内存管理函数

记录学习的点点滴滴,参考侯捷<<C++内存管理>>

我们先重载一下C++的几个内存管理函数 operator new, operator new[], operator delete, operator delete[]

1.创建一个类

class Foo
{
private:
    int _id; //4
    long _data;//4
    string _str;//4


public:
    Foo():_id(0)
    {
        cout << "default ctor.this=" << this << " id=" << _id << endl;
    }
    Foo(int a):_id(a)
    {
        cout << "ctor.this=" << this << " id=" << _id << endl;
    }

    //        virtual
    ~Foo()
    {
        cout << "dtor.this=" << this << " id=" << _id << endl;
    }

    //申请内存的函数必须是必须是静态的 调用这个函数时一般都是正在创建这个对象 所以当调用时你这个对象还不存在 所以需要声明成静态
    static void* operator new(size_t size);
    static void  operator delete(void* pdead, size_t size);
    static void* operator new[](size_t size);
    static void  operator delete[](void* pdead, size_t size);
};

 上一节提到过operator new 和 operator delete的实现是基于malloc()和free() 这边也直接在函数内声明这两个函数

    void* Foo::operator new(size_t size)//size 为将要申请的内存大小
    {
        Foo* p = (Foo*)malloc(size);
        cout <<"operator new().sizeof()="<< size << "       return=" << p <<endl;
        return p; //p 为内存起始点
    }

    void Foo::operator delete(void* pdead, size_t size)//pdead 删除点  和上面的p为同一个位置   size 为将要删除的内存大小
    {
        cout <<"operator delete.pdead=" << pdead << "       size=" << size <<endl;
        cout << endl;
        free(pdead);
    }

    void* Foo::operator new[](size_t size)
    {
        Foo* p = (Foo*)malloc(size);
        cout <<"operator new[].size="<< size <<"     return=" << p << endl;
        return p;
    }

    void Foo::operator delete[](void* pdead, size_t size)
    {
        cout<< "operator delete[].pdead=" << pdead << "     size="<< size <<endl;
        cout << endl;
        free(pdead);
    }

 

下面调用一下测试函数

    void test()
    {
        cout << "sizeof(Foo)="<<sizeof(Foo) << endl;
        Foo* p = new Foo(7);
        delete p;

        Foo* pArray = new Foo[5];
        delete [] pArray;
    }

下图示侯捷在<<C++内存管理>> 在VC6上的内存管理图片

class A
{
    int a;  
    ...
    int j;//共10个int型变量
}

 

那么这个类的Debug版实际内存应该是下图这种

这个图片分为 白色的上下cookie 上下cookie占8字节 10个灰色的data数据占40字节 12字节的补全字节 和一个橙色的4字节空位,

上面我用的是Qt的编译器

看上面log的第7行 "operator new[].size=64  return = 0x8714c0" 起始内存点在0x8714c0.但是构造函数的内存起点是0x8714c4

那么这4个字节就是上图白色部分的cookie,它的内存分配应该是下图所示

那么当我们每创建一组这样的数据,那就会有上下两个cookie(8字节),那么当我们创建多组数据的话就会有很多组cookie,那么就会占用一些花销

有一种方法减少这种内存的花销,那就是内存池操作


内存池

先说一下内存池的优点

1.减少malloc的使用,提高运行效率

2.可以指定对齐方式

3.减少内存碎片,减少cookie

创建一个Screen类

class Screen
{
public:
    Screen(int x): i(x)
    {
        //todo
    }

    int get()
    {
        return i;
    }

    void* operator new(size_t);
    void operator delete(void*, size_t);

private:
    Screen* next;//4bit
    static Screen* freeStore;
    static const int screenChunk;//想要创建多少组

private:
    int i; //4bit
};

Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;

 

重载operator new(创建内存池)和operator delete

void* Screen::operator new(size_t size)
{
    Screen* p;
    if(!freeStore)
    {
        //linked list是空的,所以申请一大块内存
        size_t chunk = screenChunk * size; //192 Screen的内存大小为8共24组  24 * 8 = 192
        freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
        cout << "startPisotion: " << p << endl;

        //将一大块内存分割成片段,当做linked list串接起来
        for(; p != &freeStore[screenChunk-1]; ++p)
        {
            p->next = p+1;
        }
        p->next = 0;
    }
    p = freeStore;
    freeStore = freeStore->next;

    return p;
}

void Screen::operator delete(void* p, size_t)
{
    //将delete object插回 free list前端
    (static_cast<Screen*>(p)) -> next = freeStore;
    freeStore = static_cast<Screen*>(p);
}

 

测试代码

void test()
{
    cout << sizeof(Screen) << endl;

    size_t const N = 10;

    Screen* p[N];

    cout << "overload operator new" << endl;
    for(int i=0; i<N; i++)
    {
        p[i] = new Screen(i);
    }

    for(int i = 0; i<10; i++)
    {
        cout << p[i] << endl;//输出每个Screen的内存起点
    }

    for(int i=0; i<N; i++)
    {
        delete p[i];
    }

    cout << "glob operator new" << endl;

    Screen* q[N];

    for(int i=0; i<N; i++)
    {
        q[i] = ::new Screen(i);
    }

    for(int i = 0; i<10; i++)
    {
        cout << q[i] << endl;
    }

    for(int i=0; i<N; i++)
    {
        ::delete q[i];
    }
}

 测试结果

你会发现调用全局的和调用重载的函数结果差别会很大

全局operator new的默认对齐方式是16字节对齐而且会发现没有了cookie,而且减少了malloc得使用提高使用效率

测试代码

namespace wzj03_class12_15 {

class Screen
{
public:
    Screen(int x): i(x)
    {
        //todo
    }

    int get()
    {
        return i;
    }

    void* operator new(size_t);
    void operator delete(void*, size_t);

private:
    Screen* next;//4bit
    static Screen* freeStore;
    static const int screenChunk;

private:
    int i; //4bit
};

Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;

void* Screen::operator new(size_t size)
{
    Screen* p;
    if(!freeStore)
    {
        //linked list是空的,所以申请一大块内存
        size_t chunk = screenChunk * size; //192 Screen的内存大小为8共24组  24 * 8 = 192
        freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
        cout << "startPisotion: " << p << endl;

        //将一大块内存分割成片段,当做linked list串接起来
        for(; p != &freeStore[screenChunk-1]; ++p)
        {
            p->next = p+1;
        }
        p->next = 0;
    }
    p = freeStore;
    freeStore = freeStore->next;

    return p;
}

void Screen::operator delete(void* p, size_t)
{
    //将delete object插回 free list前端
    (static_cast<Screen*>(p)) -> next = freeStore;
    freeStore = static_cast<Screen*>(p);
}

void test()
{
    cout << sizeof(Screen) << endl;

    size_t const N = 10;

    Screen* p[N];

    cout << "overload operator new" << endl;
    for(int i=0; i<N; i++)
    {
        p[i] = new Screen(i);
    }

    for(int i = 0; i<10; i++)
    {
        cout << p[i] << endl;
    }

    for(int i=0; i<N; i++)
    {
        delete p[i];
    }

    cout << "glob operator new" << endl;

    Screen* q[N];

    for(int i=0; i<N; i++)
    {
        q[i] = new Screen(i);
    }

    for(int i = 0; i<10; i++)
    {
        cout << q[i] << endl;
    }

    for(int i=0; i<N; i++)
    {
        delete q[i];
    }
}

}
View Code

这段代码是封装了一个小型内存池

namespace wzj03_class12_15_1 {
class myAllocator
{
private:
    struct obj
    {
        struct obj* next;
    };

public:
    void* allocate(size_t);
    void  deallocate(void*, size_t);
private:
    obj* freeStore = nullptr;
    const int CHUNK = 5;
};

void myAllocator::deallocate(void* p, size_t size)
{
    cout << "myAllocator::deallocate" << "size: " << size <<endl;
    ((obj*)p)->next = freeStore;
    freeStore = (obj*)p;
}

void* myAllocator::allocate(size_t size)
{
    cout << "myAllocator::allocate" << "size: " << size <<endl;
    obj* p;
    if(!freeStore)
    {
        size_t chunk = CHUNK * size;
        freeStore = p = (obj*)malloc(chunk);

        for(int i=0; i<(CHUNK-1); ++i)
        {
            p->next = (obj*)((char*)p + size);
            p = p->next;
        }

        p->next = nullptr;
    }
    p= freeStore;
    freeStore = freeStore -> next;

    return p;
}

class Foo
{
public:
    long L;
    string str;
    static myAllocator myAlloc;
public:
    Foo(long l): L(l)
    {
       //todo
    }

    static void* operator new(size_t size)
    {
        return myAlloc.allocate(size);
    }

    static void operator delete(void* pdead, size_t size)
    {
        return myAlloc.deallocate(pdead, size);
    }
};
myAllocator Foo::myAlloc;

void test()
{
    cout << sizeof(Foo) << endl;

    size_t const N = 5;

    Foo* p[N];

    cout << "overload operator new" << endl;
    for(int i=0; i<N; i++)
    {
        p[i] = new Foo(i);
    }

    for(int i = 0; i<N; i++)
    {
        cout << p[i] << endl;
    }

    for(int i=0; i<N; i++)
    {
        delete p[i];
    }

    cout << "glob operator new" << endl;

    Foo* q[N];

    for(int i=0; i<N; i++)
    {
        q[i] = ::new Foo(i);
    }

    for(int i = 0; i<N; i++)
    {
        cout << q[i] << endl;
    }

    for(int i=0; i<N; i++)
    {
        ::delete q[i];
    }
}

}
View Code
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值