Chapter12:动态内存
- 智能指針——shared_ptr
為了更容易地使用動態內存,新的標準提供了智能指針來管理動態對象。智能指針的行為類似常規指針,重要的區別是它負責自動釋放指向的對象。
?
智能指針的使用方式與普通指針類似。解引用一個智能指針返回它指向的對象。
1 if (p1 && p1->empty())
?
最安全的分配和使用動態內存的方法是調用一個名為make_shared的標準庫函數
make_shared<T>(args); //返回一個shared_ptr, 指向一個動態分配的類型為T的對象,使用args初始化此對象。
?
shared_ptr自動銷毀所管理的對象,還會自動釋放相關聯的內存。
shared_ptr在無用之后仍然保留的一種可能是:你將shared_ptr存放在一個容器中,隨后重排了容器,從而不在需要某些元素。這個時候你應該確保使用erase刪除那些不再需要的shared_ptr的元素。
?
?
- 使用了動態生存期的資源的類
(三種情況)
1. 程序不知道自己需要使用多少對象;例如容器類。
2. 程序不知道所需對象的準確類型;例如派生與繼承。
3. 程序需要在多個對象間共享數據;到目前為止,我們使用過的類中,分配的資源都與對應對象生存期一致;但某些類分配的資源具有與原對象相獨立的生存期,即:當某個對象被銷毀時,我們不能單方面地銷毀底層數據。
定義需要共享數據的類時,重點要考慮的是初始化、賦值、拷貝、銷毀等拷貝控制得問題。
?
?
- 直接管理內存:new/delete
初始化:
默認情況下,動態分配的對象時默認初始化的,這意味著內置類型或組合類型的對象的值是未定義的,而類類型對象將用默認構造函數進行初始化;
對于定義了自己的構造函數的類類型來說,要求值初始化沒有意義;不管采用什么形式,對象都會通過默認構造函數來初始化;
1 string *ps1 = new string; //默認初始化為空string 2 string *ps2 = new string(); //默認初始化為空string 3 4 int* pi1 = new int;//默認初始化;*pi1的值未定義 5 int* pi1 = new int(); //值初始化為0
?
delete:
釋放一塊并非new分配的內存,或者將相同的指針值釋放多次,其行為是未定義的。
動態對象的生存期知道被釋放時為止;
delete之后的指針值就變為無效了。雖然指針已經無效,但是很多機器上指針仍然保存著動態內存的地址。此時指針就變為空懸指針(dangling pointer),最好在delete之后將nullptr賦予指針。
動態內存的一個基本問題是:可能多個直著你指向相同的內存,而在實際系統中,查找相同內存的所有指針是異常困難的。
?
?
- shared_ptr+new
接受指針參數的智能指針構造函數是explicit的,因此我們不能將一個內置指針隱式轉換為一個智能指針,必須使用直接初始化形式。
1 shared_ptr<int> p1 = new int(1024);//錯誤,必須使用直接初始化形式 2 shared_ptr<int> p2(new int(1024));//正確,使用直接初始化形式
不要混合使用普通指針和智能指針——以防止同一塊內存綁定到多個獨立創建的shared_ptr上——推薦使用make_shared,而不是new。
1 void process(shared_ptr<int> ptr) 2 {//參數按值傳遞,發生拷貝操作 3 } 4 5 shared_ptr<int> p(new int(42)); 6 process(p);//在process中引用計數為2 7 int i = *p;//引用計數為1 8 9 int *x(new int(42));//x是普通指針 10 process(x);//錯誤 11 process(shared_ptr<int>(x));//會釋放x的內存 12 int j = *x;
當將一個shared_ptr綁定到一個普通指針時,我們就將內存管理責任交給了這個shared_ptr。一旦這樣做了,我們就不應該再使用內置指針來訪問shared_ptr所指向的內存了;
使用一個內置指針來訪問一個智能指針所負責的對象時很危險的,因為我們無法知道對象何時會被銷毀。
?
不要使用get初始化另一個智能指針或為智能指針賦值——以防止同一塊內存綁定到多個獨立創建的shared_ptr上。
?
unique():在改變對象之前,我們要檢查自己是否是當前對象的僅有的用戶。如果不是,在改變之前,我們可能需要制作一份新的拷貝。
1 if (!p.unique()) 2 p.reset(new string(*p));
?
智能指針使用規范:
1. 不使用相同的內置指針值初始化(或reset)多個智能指針;
2. 不 delete get()返回的指針;
3. 不使用get()初始化或reset另一個智能指針;
4. 如果使用了get()返回的指針,記住當最后一個對應的智能指針銷毀后,你的指針就變成無效了;
5. 如果你使用智能指針管理的資源不是new分配的內存,記住傳遞一個刪除器給它。
?
?
- 動態數組
標準庫提供了一個可以管理new分配的數組的unique_ptr版本。為了用一個unique_ptr管理動態數組,我們必須在對象類型后面加一對空括號:
unique_ptr<int []> up(new int[10]>; up.release();//自動用delete[]銷毀其指針
與unique_ptr不同,shared_ptr不直接支持管理動態數組。如果希望使用shared_ptr管理動態數組,必須提供自己定義的刪除器:
1 shared_ptr<int> sp(new int[10], [](int *p) {delete[] p; }); 2 sp.reset();//使用lambda釋放數組
1 for (size_t i = 0; i != 10; ++i) 2 { 3 up[i] = i; 4 *(sp.get() + i) = i; 5 }
?
?
- allocator類
new有一些靈活性上的局限,其中一方面表現在它將內存分配和對象構造組合在一起。類似的,delete將對象析構和內存釋放組合在一起;
當分配一大塊內存時,我們通常計劃在這塊內存上按需構造對象。在此情況下,我們希望將內存分配和對象構造分離。這意味著我們可以分配大塊內存,但只在真正需要時才執行對象創建工作。
allocator的兩個伴隨算法:
1 uninitialized_copy(b, e, b2);//從迭代器b和e指定的范圍拷貝元素到迭代器b2指定的未構造的原始內存中。 2 uninitialized_copy_n(b, n, b2)
1 uninitialized_fill(b, e, t);//在迭代器b和e指定的原始內存范圍內創建對象,值均為t的拷貝; 2 uninitialized_fill_n(b, n, t)
?
轉載于:https://www.cnblogs.com/wangyanphp/p/5831109.html
總結
以上是生活随笔為你收集整理的Chapter12:动态内存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 斗罗大陆魂师对决剑尘心用什么魂骨?
- 下一篇: ASP.NET优化