【C++】 69_技巧: 自定義內存管理

筆試題 一

統計對象中某個成員變量的訪問次數

遺失的關鍵字

  • mutable 是爲了突破 const 函數的限制而設計的
  • mutable 成員變量將永遠處於可改變的狀態
  • mutable 在實際的項目開發中被嚴謹濫用

  • mutable 的深刻分析ios

    • mutable 成員變量破壞了只讀對象的內部狀態
    • const 成員函數保證只讀對象的狀態不變性
    • mutable 成員變量的出現沒法保證狀態不變性

編程實驗: 成員變量的訪問統計

mutable 的實現:面試

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    int m_value;
    mutable int m_count;        // 注意這裏!
public:
    Test(int value = 0)
    {
        m_value = value ;
        m_count = 0;
    }
    
    int getValue() const 
    {
        m_count ++;
    
        return m_value;
    }
    
    void setValue(int value)
    {
        m_count ++;
        
        m_value = value;
    }
    
    int getCount() const
    {
        return m_count;
    }
};

int main()
{
    Test t;
    
    t.setValue(10);
    
    cout << "t.m_value = " << t.getValue() << endl;
    cout << "t.m_vount = " << t.getCount() << endl;
    
    const Test ct(200);
    
    cout << "ct.m_value = " << ct.getValue() << endl;
    cout << "ct.m_vount = " << ct.getCount() << endl;

    return 0;
}
輸出:
t.m_value = 10
t.m_vount = 2
ct.m_value = 200
ct.m_vount = 1

統計方式的改進:編程

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    int m_value;
    int * const m_pCount;          // 注意這裏!
public:
    Test(int value = 0) : m_pCount(new int(0))
    {
        m_value = 0;
    }
    
    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;
    }
};

int main()
{
    Test t;
    
    t.setValue(10);
    
    cout << "t.m_value = " << t.getValue() << endl;
    cout << "t.m_vount = " << t.getCount() << endl;
    
    const Test ct(200);
    
    cout << "ct.m_value = " << ct.getValue() << endl;
    cout << "ct.m_vount = " << ct.getCount() << endl;

    return 0;
}
輸出:
t.m_value = 10
t.m_vount = 2
ct.m_value = 0
ct.m_vount = 1

分析:
int * const m_pCount;         ==> 定義指針常量
*m_pCount = *m_pCount + 1;    ==> 修改指針常量指向的內存空間處的值
                              ==> 對象內部狀態沒有發生改變

面試題 二

new 關鍵字建立出來的對象位於什麼地方呢?
  • 堆(默認)
  • 全局數據區

被忽略的事實

  • new / delete 的本質是 C++ 預約義的操做符
  • C++ 對這兩個操做符作了嚴格的行爲定義數組

    • new :函數

      1. 獲取足夠大的內存空間(默認爲堆空間)
      2. 在獲取的空間中調用構造函數建立對象
    • delete :spa

      1. 調用析構函數銷燬對象
      2. 歸還對象所佔用的空間(默認爲堆空間)

  • 在 C++ 中可以重載 new / delete 操做符設計

    • 全局重載(不推薦)
    • 局部重載(針對具體類進行重載)

重載 new / delete 的意義在於改變更態對象建立時的內存分佈方式指針

  • new / delete 的重載方式

默認爲靜態成員函數code

// static member function
void* operator new(unsigned int size)
{
    void* ret = NULL;
    
    /** ret point to allocated memory  */
    
    return ret;
}

// static member function
void operator delete(void* p)
{
    /** free the memory which is pointed by p */
}

編程實驗: 靜態存儲區中建立對象

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    static const unsigned int COUNT = 4;
    static unsigned char c_buffer[];
    static unsigned char c_map[];
    
    int m_value;
public:
    void* operator new(unsigned int size)
    {
        void* ret = NULL;
        
        for(int i=0; i<COUNT; i++)
        {
            if( !c_map[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 )
        {
            unsigned char* mem = reinterpret_cast<unsigned 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;
            }
        }
    }
};

unsigned char Test::c_buffer[Test::COUNT] = {0};
unsigned char Test::c_map[Test::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;
}
輸出:
===== Test Single Object ====
Succeed to allocate memory: 0x804a0d4
succeed to free memory: 0x804a0d4
==== Test Object Array ====
Succeed to allocate memory: 0x804a0d4
pa[0] = 0x804a0d4
Succeed to allocate memory: 0x804a0d8
pa[1] = 0x804a0d8
Succeed to allocate memory: 0x804a0dc
pa[2] = 0x804a0dc
Succeed to allocate memory: 0x804a0e0
pa[3] = 0x804a0e0
pa[4] = 0
delete 0x804a0d4
succeed to free memory: 0x804a0d4
delete 0x804a0d8
succeed to free memory: 0x804a0d8
delete 0x804a0dc
succeed to free memory: 0x804a0dc
delete 0x804a0e0
succeed to free memory: 0x804a0e0
delete 0
如何在指定的地址上建立C++對象?

  • 解決方案對象

    • 在類中重載 new / delete 操做符
    • 在 new 的操做符重載函數中返回指定的地址
    • 在 delete 操做符重載中標記對應的地址可用

編程實驗: 自定義動態對象的存儲空間

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

using namespace std;

class Test
{
private:
    static unsigned int c_count;
    static unsigned char* c_buffer;
    static unsigned char* c_map;
    
    int m_value;
public:
    static bool SetMemorySource(unsigned char* memory, unsigned int size)
    {
        bool ret = false;
        
        c_count = size / sizeof(Test);
        
        ret = (c_count && (c_map = reinterpret_cast<unsigned char*>(calloc(c_count, sizeof(unsigned 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 )
            {
                unsigned char* mem = reinterpret_cast<unsigned 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 = NULL;
unsigned char* Test::c_buffer = NULL;
unsigned char* Test::c_map = NULL;

int main()
{
    unsigned 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: 0xbfbbacb0
succeed to free memory: 0xbfbbacb0
==== Test Object Array ====
Succeed to allocate memory: 0xbfbbacb0
pa[0] = 0xbfbbacb0
Succeed to allocate memory: 0xbfbbacb4
pa[1] = 0xbfbbacb4
Succeed to allocate memory: 0xbfbbacb8
pa[2] = 0xbfbbacb8
pa[3] = 0
pa[4] = 0
delete 0xbfbbacb0
succeed to free memory: 0xbfbbacb0
delete 0xbfbbacb4
succeed to free memory: 0xbfbbacb4
delete 0xbfbbacb8
succeed to free memory: 0xbfbbacb8
delete 0
delete 0

被忽略的事實

  • new[] / delete[] 與 new / delete 徹底不一樣

    • 動態對象數組建立經過 new[] 完成
    • 動態對象數組的銷燬經過 delete[] 完成
    • new[] / delete[] 可以被重載,進而改變內存管理方式

new[] / delete[] 的重載方式

默認爲靜態成員函數

// static member function
void* operator new[] (unsigned int size)
{
    return malloc(size);
}

// static member function
void operator delete[] (void* p)
{
    free(p);
}
  • 注意事項

    • new[] 實際須要返回的內存空間可能比指望的多
    • 對象數組佔用的內存中須要保存數組信息(數組長度)
    • 數組信息用於肯定構造函數和析構函數的調用次數

編程實驗: 動態數組的內存管理

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

using namespace std;

class Test
{
private:
    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()
{
    Test* pt = NULL;
    
    pt = new Test;
    
    delete pt;
    
    pt = new Test[5];
    
    delete[] pt;

    return 0;
}
輸出:
operator new: 4
operator delete: 0x9e3c008
operator new[]: 24               ;注意這裏!
operator delete[]: 0x9e3c018

小結

  • new / delete 的本質爲操做符
  • 能夠經過全局函數重載 new / delete (不推薦)
  • 能夠針對具體的類重載 new / delete
  • new[] / delete[] 與 new / delete 徹底不一樣
  • new[] / delete[] 也是能夠被重載的操做符
  • new[] 返回的內存空間可能比指望的要多

以上內容參考狄泰軟件學院系列課程,請你們保護原創!

相關文章
相關標籤/搜索