第69课.技巧:自定义内存管理

Posted huangdengtao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第69课.技巧:自定义内存管理相关的知识,希望对你有一定的参考价值。

1.统计对象中某个成员变量的访问次数

注意:对象(普通对象,只读对象)

eg:

#include <iostream>
#include <string>

using namespace std;

class Test
{
    int m_value;
    int * const m_pCount;
public:
    Test(int value = 0) : m_pCount(new int(0))
    {
        m_value = value;
    }
    
    int getValue() const
    {
        *m_pCount = *m_pCount + 1;
        return m_value;
    }
    
    void setValue(int value)
    {
        *m_pCount = *m_pCount + 1;
        m_value = value;
    }
    
    int getCount() const
    {
        return *m_pCount;
    }
    
    ~Test()
    {
        delete m_pCount;
    }
};

int main()
{
    // 普通对象
    Test t;
    
    t.setValue(100);
    
    cout << "t.m_value = " << t.getValue() << endl;
    cout << "t.m_count = " << t.getCount() << endl;
    
    // 只读对象
    const Test ct(200);
    cout << "ct.m_value = " << ct.getValue() << endl;
    cout << "ct.m_count = " << ct.getCount() << endl;
    
    return 0;
}

技术图片

2.new关键字创建出来的对象位于什么地方?

答案:可以位于静态存储区,堆去,栈区

new与delete

a.new/delete的本质是c++预定义的操作符(可重载)
b.c++对这两个操作符做了严格的行为定义

?new
??1).获得足够大的内存空间(默认为堆空间
??2).在获取的空间中调用构造函数创建对象

?delete
??1).调用析构函数销毁对象
??2).归还对象所占用的空间(默认为堆空间

c.在c++中能够重载new/delete操作符
?1).全局重载(不推荐)
?2).局部重载(针对具体类进行重载)
重载new/delete的意义在于改变动态对象创建时的内存分配方式

new/delete的重载方式

// 默认为静态成员函数
void* operator new (unsigned int size)
{
    void* ret = NULL;

    /*    ret point to allocated memory    */
    
    return ret;
}

// 默认为静态成员函数
void operator delete(void* p)
{
    /*    free the memory which is pointed by p    */
}

new关键字创建的对象位于静态存储区

eg:

#include <iostream>
#include <string>

using namespace std;

class Test
{
    static const unsigned int COUNT = 4;
    static char c_buffer[];
    static char c_map[];
    
    int m_value;
public:
    void* operator new (unsigned int size)
    {
        void* ret = NULL;
        
        for(int i = 0; i < COUNT; i++)
        {
            c_map[i] = 1;
            
            ret = c_buffer + i * sizeof(Test);
            
            cout << "succeed to allocate memory: " << ret << endl;
            
            break;
        }
        
        return ret;
    }
    
    void operator delete(void* p)
    {
        if(p != NULL)
        {
            char* mem = reinterpret_cast<char*>(p);
            int index = (mem - c_buffer) / sizeof(Test);
            int flag  = (mem - c_buffer) % sizeof(Test);
            
            if((flag == 0) && (0 <= index) && (index < COUNT))
            {
                c_map[index] = 0;
                
                cout << "succeed to free memory: " << p << endl;
            } 
        }
    }
};

char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};
char Test::c_map[COUNT] = {0};

int main()
{
    cout << "===== Test Single Object =====" << endl;
    
    Test* pt = new Test;
    
    delete pt;
    
    cout << "===== Test Object Array =====" << endl;
    
    Test* pa[5] = {0};
    
    for(int i = 0; i < 5; i++)
    {
        pa[i] = new Test;
        
        cout << "pa[" << i << "] = " << pa[i] << endl; 
    }

    for(int i = 0; i < 5; i++)
    {
        cout << "delete" << pa[i] << endl;
        
        delete pa[i];
    }
    
    return 0;
}

技术图片

如何在指定的地址上创建c++对象(new在栈区创建对象)

解决方案:
?在类中重载new/delete操作符;在new的操作符重载函数中返回指定地址;在delete操作符重载中标记对应地址可用。

知识点:
calloc:
a.参数 : (个数,大小),例;(20, int);申请一个数组成员为20个int大小
b.初始化: 在动态分配完内存后,自动初始化该内存空间为零。
c.返回值: 函数返回值是一个数组。

eg:

#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

class Test
{
    static unsigned int c_count;
    static char* c_buffer;      // 因为是动态的,自定义地址。所以这里使用指针
    static char* c_map;         // 因为是动态的,自定义地址。所以这里使用指针
    
    int m_value;
public:
    static bool SetMemorySource(char* memory, unsigned int size)
    {
        bool ret = false;
        
        c_count = size / sizeof(Test);      // 确保size至少能存下一个Test
        
        // c_map初始化,一个数组。成员是c_count个char
        ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char)))));
        
        if(ret)
        {
            c_buffer = memory;
        }
        else
        {
            // 如果指定到地址不能装下一个Test,或者c_map数组赋值失败。
            free(c_map);
            
            c_map = NULL;
            c_buffer = NULL;
            c_count = 0;
        }
        
        return ret;
    }
    
    void* operator new (unsigned int size)
    {
        void* ret = NULL;
        
        if(c_count > 0)
        {
            for(int i = 0; i < c_count; i++)
            {
                if(!c_map[i])
                {
                    c_map[i] = 1;
                    
                    ret = c_buffer + i * sizeof(Test);
                    
                    cout << "succeed to allocate memory: " << ret << endl;
                    
                    break;
                }
            }
        }
        else
        {
            ret = malloc(size);
        }
    
        return ret;
    }
    
    void operator delete(void* p)
    {
        if(p != NULL)
        {
            if(c_count > 0)
            {
                char* mem = reinterpret_cast<char*>(p);
                int index = (mem - c_buffer) / sizeof(Test);
                int flag = (mem - c_buffer) % sizeof(Test);
                
                if((flag == 0) && (0 <= index) && (index < c_count))
                {
                    c_map[index] = 0;
                    
                    cout << "succeed to free memory: " << p << endl;
                }
             }
             else
             {
                free(p);
             }
        }
    }
};

unsigned int Test::c_count = 0;
char* Test::c_buffer = NULL;        // 初始为空
char* Test::c_map = NULL;

int main()
{
    char buffer[12] = {0};
    
    Test::SetMemorySource(buffer, sizeof(buffer));
    
    cout << "===== Test Single Object =====" << endl;
     
    Test* pt = new Test;
    
    delete pt;
    
    cout << "===== Test Object Array =====" << endl;
    
    Test* pa[5] = {0};
    
    for(int i=0; i<5; i++)
    {
        pa[i] = new Test;
        
        cout << "pa[" << i << "] = " << pa[i] << endl;
    }
    
    for(int i=0; i<5; i++)
    {
        cout << "delete " << pa[i] << endl;
        
        delete pa[i];
    }
    
    return 0;
}

技术图片

3.new[]/delete 与 new/delete

new[]/delete 与 new/delete完全不同
a.动态对象数组创建通过new[]完成
b.动态对象数组的销毁通过delete[]完成
c.new[]/delete[]能够被重载,进而改变内存管理方式

注意:

a.new[]实际需要返回的内存空间可能比期望的要多。因为对象组占用的内存中需要保存数组信息
b.数组信息用于确定构造函数和析构函数的调用次数

重载方式

 // 默认为静态成员函数
void* operator new[] (unsigned int size)
{
    void* ret = NULL;

    /*    ret point to allocated memory    */
    
    return ret;
}

// 默认为静态成员函数
void operator delete[](void* p)
{
    /*    free the memory which is pointed by p    */
}

eg:

#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

class Test
{
    int m_value;
public:
    Test()
    {
        m_value = 0;
    }
    
    ~Test()
    {
    }
    
    void* operator new (unsigned int size)
    {
        cout << "operator new: " << size << endl;
        
        return malloc(size);
    }
    
    void operator delete (void* p)
    {
        cout << "operator delete: " << p << endl;
        
        free(p);
    }
    
    void* operator new[] (unsigned int size)
    {
        cout << "operator new[]: " << size << endl;
        
        return malloc(size);
    }
    
    void operator delete[] (void* p)
    {
        cout << "operator delete[]: " << p << endl;
        
        free(p);
    }
};

int main(int argc, char *argv[])
{
    Test* pt = NULL;
    
    pt = new Test;
    
    delete pt;
    
    pt = new Test[5];
    
    delete[] pt;
    
    return 0;
}

技术图片

以上是关于第69课.技巧:自定义内存管理的主要内容,如果未能解决你的问题,请参考以下文章

VS中添加自定义代码片段——偷懒小技巧

69_堆得应用

技巧:自定义内存管理

STL容器自定义内存分配器

STL容器自定义内存分配器

STL容器自定义内存分配器