第六十九课、自定义内存管理

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

解决方法一:

#include <iostream>

using namespace std;

class Test
{
private:
    int i;
    mutable int m_count;
public:
    Test(int v)
    {
        i = v;
        m_count = 0;
    }
    
    void setI(int v)
    {
        i = v;
        m_count++;
    }
    
    int getI() const  //方便const对象调用
    {
        m_count++;
        return i;
    }
    
    int getCount() const //方便const对象调用
    {
        return m_count;
    }
};

int main()
{
    const Test t(2);//只读对象
    cout << t.getI() << endl;//2
    cout << t.getCount() << endl;//1
    
    return 0;
}
统计对象中某个成员变量的访问次数

1.遗失的关键字:mutable

(1)、mutable关键字是为了突破const函数的限制设计的

(2)、mutable关键字将永远处于可改变的状态

(3)、mutable关键字在实际项目中被严禁使用

2、mutab的深入分析

(1)、mutable破坏了只读对象内部的状态

(2)、const成员函数保证只读对象内部的状态不变性

(3)、mutable成员变量的出现无法保证状态不变性

解决方法二:

#include <iostream>

using namespace std;

class Test
{
private:
    int i;
    int * const m_count;//左数右指,const在*左边表示指针指向的地址中的数据是常量,在右说明指针本身是常量
public:
    Test(int v = 0) : m_count(new int(0))//const变量要在初始化列表中进行初始化
    {
        i = v;

    }
    
    void setI(int v)
    {
        i = v;
        *m_count = *m_count + 1;
    }
    
    int getI() const  //方便const对象调用
    {
        *m_count = *m_count + 1;//巧妙!!!只读成员函数里并没有改变成员变量的值
        return i;
    }
    
    int getCount() const //方便const对象调用
    {
        return *m_count;
    }
    
    ~Test()
    {
        delete m_count;
    }
};

int main()
{
    const Test t(2);//只读对象
    cout << t.getI() << endl;//2
    cout << t.getCount() << endl;//1
    
    return 0;
}
统计对象中对某个变量的访问次数

二、new/delete的本质是c++预定义的操作符

1、c++对这两个操作符做了严格的行为定义

new:

(1)、获取足够大的内存空间(默认为堆空间

(2)、在获取的空间中调用构造函数创建对象

delete:

(1)、调用析构函数销毁对象

(2)、归还对象所占用的空间(默认为堆空间)

2、c++中能够重载new/delete操作符(意义在于改变动态对象创建时的内存分配方式)

(1)、全局重载(不推荐)

(2)、局部重载(针对具体类进行重载)

3、new/delete的重载方式(都是static的)

#include <iostream>

using namespace std;

class Test
{
private:
    static const unsigned int COUNT = 4;//最多分配4个Test大小
    static char c_buffer[];//分配空间
    static char c_map[];//用来标记哪个空间可以用
    int m_value;
public:
    Test()
    {
    }
    
    void * operator new(unsigned int size)
    {
        void* ret = NULL;
        
        for(int i=0; i<COUNT; i++)
        {
            if( !c_map[i] )
            {
                ret = c_buffer + i*sizeof(Test);
                
                c_map[i] = 1;
                
                cout << "succed to new a object:" << 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 << "succed to delete a object: " << p << endl;
            }
        }
    }
    

};

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

int main()
{    
    Test* pa[5] = {0};
  
    for(int i=0; i<5; i++)
    {
        pa[i] = new Test;
        cout << "new " << "pa[" << i << "]=" << pa[i] << endl;
    }
    
      
    for(int i=0; i<5; i++)
    {  
        cout << "delete " << "pa[" << i << "]=" << pa[i] << endl;
        delete pa[i];
    }
    
    
    return 0;
}
//输出结果
/*
succed to new a object:0x804a0d4
new pa[0]=0x804a0d4
succed to new a object:0x804a0d8
new pa[1]=0x804a0d8
succed to new a object:0x804a0dc
new pa[2]=0x804a0dc
succed to new a object:0x804a0e0
new pa[3]=0x804a0e0
new pa[4]=0
delete pa[0]=0x804a0d4
succed to delete a object: 0x804a0d4
delete pa[1]=0x804a0d8
succed to delete a object: 0x804a0d8
delete pa[2]=0x804a0dc
succed to delete a object: 0x804a0dc
delete pa[3]=0x804a0e0
succed to delete a object: 0x804a0e0
delete pa[4]=0

*/
静态存储区分配空间

三、在指定地址上创建c++对象

1、重载new/delete

(1)、在类中重载new/delete操作符

(2)、在new操作符重载函数中返回指定地址

(3)、在delete操作符重载函数中标记对应的地址可用

#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);
        
        ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char)))));
        
        if( ret )
        {
            c_buffer = memory;
        }
        else
        {
            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(int argc, char *argv[])
{
    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;
}

//输出结果
/*
===== Test Single Object =====
succeed to allocate memory: 0xbfbc38f0
succeed to free memory: 0xbfbc38f0
===== Test Object Array =====
succeed to allocate memory: 0xbfbc38f0
pa[0] = 0xbfbc38f0
succeed to allocate memory: 0xbfbc38f4
pa[1] = 0xbfbc38f4
succeed to allocate memory: 0xbfbc38f8
pa[2] = 0xbfbc38f8
pa[3] = 0
pa[4] = 0
delete 0xbfbc38f0
succeed to free memory: 0xbfbc38f0
delete 0xbfbc38f4
succeed to free memory: 0xbfbc38f4
delete 0xbfbc38f8
succeed to free memory: 0xbfbc38f8
delete 0
delete 0

*/
在指定地址分配空间

 2、new[]/delete[]和new/delete 完全不同

(1)、动态对象数组的创建通过new[]完成

(2)、动态对象数组销毁通过delete[]完成

(3)、new[]/delete[]可以被重载,进而改变内存管理方式

注意事项:

(1)、new[]返回的地址空间可能比期望的多(原因如2、3)

 (2)、对象数组中占用的内存中需要保存数组信息(不然怎么知道调用多少次构造和析构函数?)

(3)、数组信息用于确定构造函数和析构函数的调用次数

#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;
}
//输出结果
/*
operator new: 4
operator delete: 0x8463008
operator new[]: 24
operator delete[]: 0x8463018
*/
new[]分配的空间比预期的多

四、小结

(1)、new/delete本质为操作符

(2)、可以通过全局函数重载new/delete(不推荐)

(3)、可以针对具体类重载new/delete 

(4)、new[]/delete[]与new/delete 完全不同

(5)、new[]/delete[]也是可以被重载的操作符

(6)、new[]返回的地址空间可能比期望的多

 

posted @ 2018-03-25 13:12  lgc202  阅读(475)  评论(0编辑  收藏  举报