版權聲明:尊重原創,轉載請注明來源。
目錄(?)[+]
昨天跟同事小小的研究了下關于不同平臺下的字節對齊問題,起因是遇到了一個坑,vs上沒有問題,在安卓上卻崩潰了。找了半天后發現是c++字節補齊問題,期間包括使用#PRagma pack(1)來限定字節對齊方式等各種條件,也是把我們搞的七暈八素,總算是進一步了解了c++對象結構以及編譯器的操作(有機會的話也補充下字節對齊的理解)。進而想到了智能指針,稍微了解下。第一次接觸智能指針,天知道大學期間自己有多不努力,很多知識點都得留到現在來補齊,所以還是做做筆記吧。
簡斷截說:c++的入門坑點大家都是有目共睹的,無非就是指針的理解不深導致一些野指針,內存泄露等問題,所以就不贅述。智能指針正好能夠彌補這些問題,因為它本質是存放在棧的模板對象,只是在棧內部包了一層指針。而棧在其生命周期結束時,其中的指針指向的堆內存也自然被釋放了。因而實現了智能管理的效果,不需要考慮內存問題了,其實有點類似某種單例寫法,程序運行結束,也不用考慮單例對象內存問題。
本次討論:C++11之前的auto_ptr; c++11新加的unique_ptr, shared_ptr以及weak_ptr。
頭文件:#include <memory>
auto_ptr是我第一個看的智能指針,也是標準庫里的智能指針,有許多缺陷。
首先看下結構:
從圖中可以看書也是一個模板,使用方法大致類似于vector模板。如下:
[cpp] view plain copy從上圖可以看出,該智能指針成員函數也與vector相似,很容易得出
1、base1.get():返回當前指針對象;
2、base1.release():清空當前智能指針對象,并返回類型指針。所以假如我們要正常刪除,那么需要這樣:
[cpp] view plain copy3、base1.reset():從圖中可看出,是重置智能指針,即把內存刪除,且智能指針指向空,但類型不變,所以可以這樣安全便捷地刪除:
[cpp] view%20plain copy然而繼續看,還有一個問題:auto_ptr還重載了等號操作符,由圖可知意思是把賦值智能指針的內存交給被賦值智能指針,即如下意思:
介紹之前先上一張別人的表格,來源:http://my.oschina.net/hevakelcj/blog/465978,這是c++11中的智能指針與boost庫中的比較,原本boost就是為完善auto_ptr搞得這些,現在c++11有了,也就不需要再用咯。
2.unique_ptr
C++11引入了許多便捷的功能,其中也包括這個,在用之前我們可以先看下底層:
可以清楚的看到,unique_ptr中的拷貝構造和賦值操作符delete了,所以也就意味著,他和auto_ptr有區別,控制權唯一,不能隨意轉換。用法都差不多:
[cpp] view plain copyunique_ptr<Base1> base1(new Base1); unique_ptr<Base1> base2;//但是不能用拷貝構造和等號賦值把base1賦值給base2了 但是如果想切換控制權的話也不是沒有辦法,我們可以看到還有個這樣的函數:
要理解這兩個函數,首先要理解c++11引入的move和forward;而要理解move和forward得先理解左值和右值概念。所以還是講全一點吧(已經了解的就直接跳過可以):
補充知識點(其實可以直接看我下一篇更方便理解:點擊打開鏈接):
1、左值與右值:
左值指的是既能夠出現在等號左邊也能出現在等號右邊的變量(或表達式),右值指的則是只能出現在等號右邊的變量(或表達式)。需要注意的是,左值是指表達式結束后依然存在的持久對象,而右值是指表達式結束時就不再存在的臨時對象。T& 指向的是 lvalue,而 const T& 指向的,卻可能是 lvalue 或 rvalue,左值引用&與右值引用&&(右值引用是c++11加上的)。
2、move和forward:
需要明確的是,move函數可以是用于構造函數,也可以用于賦值函數,但都需要手動顯示添加。其實move函數用直白點的話來說就是省去拷貝構造和賦值時中間的臨時對象,將資源的內存從一個對象移動到(共享也可以)另一個對象。官話是:c++11 中的 move() 是這樣一個函數,它接受一個參數,然后返回一個該參數對應的右值引用。
std::forward<T>(u) 有兩個參數:T 與 u。當T為左值引用類型時,u將被轉換為T類型的左值,否則u將被轉換為T類型右值。如此定義std::forward是為了在使用右值引用參數的函數模板中解決參數的完美轉發問題。
其實這里說的不夠清晰,下次翻譯一篇國外的解釋,閱讀下來就能很好理解move這個概念了,這里先不深入。
回到這張圖,這兩個函數體也就很明朗了——重載move版本的拷貝構造函數以及重載move版本的等號賦值函數。
意思就是:把右值的對象(right)移動給左值(_myt&),并且右值清空。
那么用法來了:
[cpp] view plain copyunique_ptr<Base1> base1(new Base1); unique_ptr<Base1> base2=move(base1);//base1變成empty unique_ptr<Base1> base3; base3 = move(base2);//base2變成empty 其它的成員函數就不一一贅述,和auto_ptr大致上是相同的。總結,某種程度來說比auto_ptr更為安全,適用部分特殊情況。
3.shared_ptr
如果完全理解了上面兩個ptr的底層,那么shared_ptr的也就容易理解多了。但是和前兩者有很大區別——
前兩者控制權唯一,切換的時候把前面的清除。而shared_ptr不會,照例看下底層:
很顯然,可以直接賦值和調用拷貝構造函數,且不會清空原本的智能指針。用法就很簡單了:
[cpp] view plain copyshared_ptr<Base1> base1(new Base1); shared_ptr<Base1> base2=base1; shared_ptr<Base1> base3; base3 = base2;//三個共享一個
有個地方需要注意,當刪除一個智能指針時,并不影響其它兩個智能指針的繼續使用。因為該片內存添加了一個引用計數,每shared_ptr一次,引用計數+1;每次調用析構函數,引用計數減一。直到最后一個智能指針刪除,才會釋放內存。
注意:
1、在繼續查看時,你會發現以下兩個函數:
其實就是和unique_ptr一樣可以通過move來切換控制權,這個時候是切換,不是共享了。
2、接下來繼續翻看,還有兩個函數:
(其實auto_ptr也有,只是一樣,沒必要截圖了)也就是說,auto_ptr和unique_ptr都可以通過move函數轉換成shared_ptr類型,當然,一樣是切換控制權的形式,即舊的置空。
用法如下:
[cpp] view plain copyauto_ptr<Base1> base1(new Base1); shared_ptr<Base1> base2=move(base1);
4.weak_ptrred_ptr
weak_ptr更像是shared_ptr的助手:
1、他不像其余三種,可以通過構造函數直接分配對象內存;他必須通過shared_ptr來共享內存。
2、沒有重載opreator*和->操作符,也就意味著即使分配到對象,他也沒法使用該對象
3、不主動參與引用計數,即,share_ptr釋放了,那么weak_ptr所存的對象也釋放了。
4、使用成員函數use_count()可以查看當前引用計數,expired()判斷引用計數是否為空。
5、lock()函數,返回一個shared_ptr智能指針:
也就是讓weak_ptr觀測shared_ptr智能指針,并且在需要時候通過lock函數返回一個shared_ptr。
6、此外,百科上說:助手類enable_shared_from_this的Shared_from_this會返回this的shared_ptr,所以只需讓要被shared_ptr管理的類繼承它即可。我倒是沒試,有興趣的可以試試,大致意思也就是這般。
新聞熱點
疑難解答