C++ primer 第五版個人筆記 第十二章 動態內存

程序用堆來存儲動態分配的對象(即那些在程序運行時分配的對象),動態對象的生存期由程序來控制,當其不再使用時,我們的代碼必須顯式地銷燬他們;

12.1 動態內存與智能指針

  1. C++中動態內存的管理是通過一對運算符new和delete來完成的,new在動態內存中爲對象分配空間並返回一個指向該對象的指針,delete接受一個動態對象的指針並銷燬該對象釋放與之關聯的內存;
  2. 新標準庫提供兩種智能指針管理動態對象,與常規指針的區別在於它負責自動釋放所指向的對象;shared_ptr允許多個指針指向同一個對象,unique_ptr則“獨佔”所指向的對象,weak_ptr是一種弱引用,指向shared_ptr所管理的對象,這三種類型都定義在<memory>頭文件中;
  3. shared_ptr類的操作詳見401頁,最安全的分配和使用動態內存你的方法是調用一個名爲make_shared的標準庫函數,類似順序容器的emplace成員函數,make_shared用其參數來構造給定類型的對象,如果不傳遞任何參數,對象就會進行值初始化;
    auto p6 = make_shared<vector<string>>(); p6指向一個動態分配的空vector<string>

    我們可以認爲每個shared_ptr都有一個關聯的計數器,通常稱其爲引用計數,當拷貝一個shared_ptr(拷貝構造的三種情況)時計數器遞增,當給shared_ptr賦予一個新值或者shared_ptr被銷燬(例如一個局部的shared_ptr離開作用域)時,計數器遞減;

    // factory函數返回一個shared_ptr指針,指向<Foo>類型的對象
    shared_ptr<Foo> factory(T arg)
    {
        return make_shared<Foo>(arg);
    }
    
    shared_ptr<Foo> use_factory(T arg)
    {
        shared_ptr<Foo> p = factory(arg);    //p計數爲1
        return p;    //return p使得p引用計數遞增了一次,及時P離開作用域遞減一次p的計數仍不爲0,因此不會釋放其內存
    }

     

  4.  由於在最後一個shared_ptr銷燬前內存都不會釋放,因此需要保證shared_ptr在無用之後就不再保留,否則會浪費內存,有一種情況是shared_ptr保存在一個容器中,容器重排後不再需要全部元素,要用erase刪除那些不在需要的shared_ptr元素;  

  5.  

    程序使用動態內存主要有三個原因:程序不知道自己有多少個對象,程序不知道所需對象的準確類型,程序需要多個對象間共享數據;    

  6. 將一個shared_ptr賦予另一個shared_ptr會遞增賦值號右側shared_ptr的引用計數,而遞減左側的引用計數。如果shared_ptr的引用計數變爲0,它所指向的對象會被自動銷燬。    

  7. 如果提供了一個括號包圍的初始化器,可以用auto從此初始化器來腿短我們想要分配的對象的類型,但是只能有單一初始化器時纔可以使用auto;

    auto p1 = new auto(obj) ; //可以,p指向與obj同類型的對象
    auto p2 = new auto{a,b,c} ; //錯誤,括號中只能有單個初始化器

      

  8.   new後面可以傳遞額外的參數,這種形式的new爲定位new,傳遞(nothrow)表示告訴它不能拋出異常,如果這種形式的new不能分配所需內存,它會返回一個空指針;

  9.  當delete一個指針後,指針值就變爲無效,但很多機器上指針仍然保存着(已經釋放了的)動態內存的地址,這時候這種指針就是所謂的空懸指針(dangling pointer);有一種方法可以避免空懸指針的問題,在指針即將要離開其作用域之前釋放掉它所關聯的內存;如果需要保留指針,可以在delete之後將nullptr賦予指針,這樣就清楚地指出指針不指向任何對象;

  10. 如果不初始化一個只能指針,它就會被初始化爲一個空指針,也可以用new返回的指針初始化智能指針,智能指針的構造函數是explicit的(說明只能直接初始化不能使用=拷貝初始化),同時一個返回shared_ptr的函數不能在其返回語句中隱式轉換一個普通指針;

  11. 智能指針定義了一個名爲get的函數,它返回一個內置指針,指向智能指針管理的對象,該指針雖然是一個內置指針但是不能用delete刪除;永遠不要用get初始化另一個智能指針或者爲另一個智能指針賦值;

  12. 自己定義釋放操作以及智能指針5個陷阱,詳見417頁;

  13. unique_ptr “擁有”它指向的對象,只能有一個unique_ptr指向一個給定的對象,unique_ptr不支持普通的拷貝與賦值操作

    unique_ptr<string>p1(new string("stegosaurus"));
    unique_ptr p2(p1); //錯誤,不支持拷貝
    unique_ptr p3 = p2; //錯誤,不支持賦值

     

12.2 動態數組

  1. 使用new分配對象數組要在類型名後使用方括號如下所示:

    int *pia = new int[get_size()];  //get_size()確定對象數目,必須是整型但不必是常量
    
    typedef int arrT [42] ;  //arrT表示 int[42]類型,即一個有42個int的數組
    int *p = new arrT; 

    對於這裏new int[]的理解,不能認爲是分配了一個數組對象,而是得到一個數組元素類型的指針,指向第一個int元素 

     

  2.  動態數組不是數組類型,不能對動態數組調用begin或end,這些函數使用數組維度來返回指向首元素和尾後元素的指針。出於相同的原因,也不能用範圍for語句來處理動態數組中的元素;

  3.  

     動態分配一個空數組是合法的, char *cp = new char[0]; 正確但是cp不能解引用;此時cp保證與new返回的其他任何指針都不相同,就像尾後指針一樣; 

  4. 爲了釋放動態數組,需要在數組名前加上一個空方括號對; delete[] pa; pa必須指向一個動態分配的數組或爲空;數組中的元素按逆序銷燬,即最後一個元素首先被銷燬,然後倒數第二個,以此類推;

12.3 文本查詢程序

  1. 重點弄清楚shared_ptr在這個程序裏的用途

 

 

 

 

 

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