• 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
  • 相关阅读:
    推荐系统相关算法
    特征的生命周期
    数学知识索引
    蓄水池(Reservoir_sampling)抽样算法简记
    数赛刷题代码学习及课程学习链接
    逻辑回归(LR)总结复习
    我的面试问题记录
    开发中遇到的一些问题
    K-Means聚类和EM算法复习总结
    常见概率分布图表总结
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/7795616.html
Copyright © 2020-2023  润新知