C++內存管理-new,delete,new[],placement new的簡單使用


技術在於交流、溝通,本文為博主原創文章轉載請注明出處並保持作品的完整性

首先,我們先看一下C++應用程序,使用memory的途徑如下圖所示

C++應用程序中申請內存基於分配器的實現(std::allocator),而分配器基於C++primitives(new,new[]...),c++primitives基於C語言中的malloc/free..,當然越底層的函數效率越高.

那我們會想,直接用最底層的實現多好,效率還高.但如果你直接調用底層的函數去實現功能,雖然你的效率提高了,但你的程序的可移植性就會相應的降低.

不可否認底層語言的實現,體現出一定的個人能力.但過多的篇幅去實現底層語言,會影響開發效率.

 


 

 

下面說一下C++三種申請內存的工具,然后介紹一下這三種工具的簡單使用

A.new operator, delete operator : 申請內存+構造函數

B.new[], delete[] : 用於數組類的操作

C.placement new : 定點new


 

一 new operator, delete operator,

分配內存的函數的對應關系如下圖,下面主要介紹一下下面這些函數的簡單使用

int main()
{
    void* p1 = malloc(512); //512bytes
    free(p1);

    complex<int>* p2 = new complex<int>; //one object
    delete p2;

    void* p3 = ::operator new(512);// 512bytes
    ::operator delete(p3);

    //一下函數都是non-static,一定要通過object調用,分配7個int的內存
    void* p4 = allocator<int>().allocate(7);//allocator<int>()創建臨時對象
    allocator<int>().deallocate((int*)p4,7);

    return 0;
}

 

 


 

 

我們都知道 new = operator new + 構造函數,delete = 析構函數 + operator delete .如果以代碼的形式表現出來應該是這樣

比如我們創建一個復數類,

Complex* pc = new Complex(1,2);

 

那么編譯器會將上面的代碼翻譯成

    try
    {
        void* mem = operator new(sizeof(Complex)); //allocate
        
        pc = static_cast<Complex*>(mem); //cast
                
        pc->Complex::Complex(1,2); //只有編譯器可以直接調用構造函數
    
    }
    catch (std::bad_alloc)
    {
        //申請內存失敗...
    }

 

釋放內存

delete pc;

 

編譯器翻譯為

    pc->~Complex();//先調用析構函數
    operator delete(pc);// operator delete的實現基於free()

 

 


二 new[]和delete[]

new[],主要運用於數組的申請內存,

如class A 當我們調用

A* p = new A[3];//那么就會申請3個class A的內存並調用3次 class A的構造函數

當我們調用 delete[]時

delete[] p; //就會調用3次析構函數 並釋放內存(3個class A的內存)

 

 

如果我們釋放時沒有加[] 

delete p;

 

它同樣會釋放3個class A的內存,但是之后調用其一個構造函數(如果其構造函數內有其他釋放內存的操作,那么我們不加[]就會造成內存泄漏)

看一下測試代碼

    class A
    {
    public:
        int id;

        A() : id(0)
        {
            cout << "default ctor.this=" << this << " id=" << id << endl;
        }

        A(int i): id(0)
        {
            cout << "ctor.this=" << this << " id=" << id << endl;
        }

        ~A()
        {
            cout << "dtor.this=" << this << " id=" << id << endl;
        }
    };

 

測試

    void test_class()
    {
        A* buf = new A[3]; //默認構造函數調用3次 調用順序 0-1-2
                           //A必須有默認構造函數 引文new A[3]調用的是默認構造函數

        A* tmp = buf;//記錄A數組的起點位置

        cout << "buf=" << buf << " tmp=" << tmp << endl;

        for(int i = 0; i < 3; i++)  
        {
            new(tmp++)A(i); //placement new
        }

        cout << "buf=" << buf << " tmp=" << tmp << endl;

        delete[] buf;
    }

 

輸出結果

我們會發現 delete[] 的順序與 new[] 的順序相反,placement后面再說

那么我們這個使用不寫[]呢,看看輸出結果會怎么樣

上面的delete 沒有寫[], 3個class A的內存是釋放了,但是只調用了一次析構函數.

 


 

 

三 placement new

placement new允許我們將object創建與 已經申請好的內存中,但是沒有所謂的 placenment delete,因為根本沒有分配內存,所以沒有placement delete

但是有被稱作的placement delete后面說.先看一下placement new

char* buf = new char[sizeof(A) * 3];//申請了3個A的內存

A* pc = new(buf)A();//運用申請好的buf的內存,在buf上賦值

 

 

 上面的new(buf)A();就是placement new.

編譯器遇到上面代碼會翻譯成

A * pc;
try {
    void* men = operator new(sizeof(A), buf); //借用內存
    pc = static_cast<A*>(mem);//安全轉換
    pc->A::A();//構造函數
}
catch (std::bad_alloc){
    //若失敗 不執行構造函數
}

 

以上就是三種C++申請內存工具的介紹

 

參考侯捷<<C++內存管理>> 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM