• 第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;
    }
    

  • 相关阅读:
    Shell与if相关参数
    Linux盘符漂移问题
    shell脚本,每5个字符之间插入"|",行末不插入“|”
    paste:linux合并两个文件中的列(左右合并)
    关于bc 的scale .
    RxJS与观察者模式
    什么是虚拟DOM
    JS设计模式
    JS自定义事件
    原生js实现拖拽功能
  • 原文地址:https://www.cnblogs.com/huangdengtao/p/12040275.html
Copyright © 2020-2023  润新知