動態內存與智能指針

轉自:http://m.blog.csdn.net/River_Lethe/article/details/78733914

程序中內存分配有以下五種:

  • 棧區
    存放局部變量、函數參數等,由編譯器自動分配,變量離開作用域後自動收回內存,可分配的內存空間較小。
    void func(int _a);
    int main()
    {
        int a = 5;       //局部變量a存放在棧區
        func(a);        //參數值存放在棧區
    }
  • 堆區 
    由程序員手動分配與回收,分配內存後生存期直到手動收回內存或程序終止,程序終止後由OS收回內存。可分配的內存空間大。
  • int *p = new int(5);   //new分配的動態內存在堆區
                           //指針變量p本身存放在棧區
  • 全局區(靜態區) 
    存放全局變量、靜態變量,由編譯器自動分配,生存期從定義開始直到程序結束。
  • int a = 10;       //全局變量a存放在全局區
    int main()
    {
        static int b = 5;   //局部靜態變量b存放在靜態區
    }
  • 文字常量區 
    字符串常量存放在這裏,程序結束後由系統釋放。
  • char s[] = "abac";   //字符串常量"abac"存放在文字常量區
                         //字符數組存放在棧區
  • 程序代碼區
    存放函數體的二進制代碼

智能指針

其中堆區的內存是程序員手動分配的,在學習智能指針之前,我們用裸指針管理這塊內存,分配的內存使用完畢後必須手動的將它釋放掉,否則會導致內存泄漏;若果有兩個指針指向同一塊內存,在一個指針將這塊內存釋放掉後,另一個指針就成了空懸指針,解引用空懸指針會造成不可知的錯誤,過去使用裸指針只能由程序員來儘量避免這兩個錯誤。

智能指針的設計就是爲了解決這樣的問題 
智能指針與裸指針的最大不同就是智能指針管理它所指內存的生命週期,當一塊內存不再被需要後,智能指針就會自動的釋放它。

智能指針分爲 shared_ptr(共享指針) 和 unique_ptr(獨佔指針)

  • 共享指針 
    是指由幾個關聯的共享指針共享一塊內存,用use_count(引用計數)來表示有幾個共享指針使用這塊內存,只有當所有共享指針都不指向這塊內存後,引用計數變0,這塊內存纔會被自動釋放。

  • 獨佔指針 
    是指由一個指針獨佔一塊內存,當獨佔指針不再指向這塊內存時,自動將其釋放。注意獨佔指針有個方法(u.release())可以放棄對內存的控制權,並返回指向這塊內存的指針(供其他指針使用),但並不釋放內存。

  • 弱指針 
    同時標準庫還定義了弱指針(weak_ptr),之所以稱作弱指針是因爲它並不控制所指內存的生存期,這一點與裸指針十分相似,但弱指針上定義了w.use_count、w.lock()等方法讓我們在使用弱指針之前就知道所指內存有沒有被釋放,只有在內存沒有被釋放的時候我們才使用它,避免了裸指針的空懸指針錯誤。

弱指針的一個重要使用場景就是解決共享指針循環引用造成的內存泄漏。 
考慮下面的一段程序

class A
{
public:
    shared_ptr<A> pre;
    shared_ptr<A> next;
    ~A()
    {
        cout << "Destructed." << endl;
    }
};

int main()
{
    //一個作用域
    {
        shared_ptr<A> sp_a1(new A),sp_a2(new A);
        sp_a1->next = sp_a2;
        sp_a2->pre = sp_a1;
    }
    //我們期望在離開這個作用域之後,sp_a1和sp_a2會釋放它們控制的內存

    system("pause");
    return 0;
}
//在離開作用域後,程序沒有沒有輸出析構函數中的Destructed
//原因是這兩塊內存上的共享指針構成了循環引用

共享指針循環引用

這幾個指針間構成了一個死鎖,這兩塊內存直到程序結束前都不會被釋放

使用弱指針就可以解決這樣的問題

class A
{
public:
    weak_ptr<A> pre;    //弱指針不具有對象的生命週期控制權
    weak_ptr<A> next;   //避免形成死鎖
    ~A()
    {
        cout << "Destructed." << endl;
    }
};

allocator類

在用new分配內存的時候發生了三件事:

  1. 申請一塊內存
  2. 構造內存上的對象
  3. 返回指向這塊內存的指針

同樣在用delete時也會:

  1. 執行內存上對象的析構函數
  2. 釋放這塊內存

allocator類的作用就是將 分配/回收 內存的操作與 構造/析構 對象的操作分開來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章