為什么需要智能指針
對于定義的局部變量,當作用域結束之后,就會自動回收,這沒有什么問題。
當時用new delete的時候,就是動態分配對象的時候,如果new了一個變量,但卻沒有delete,這會造成內存泄露。
特別是當大型項目,會使用多個指針指向同一塊內存區域的時候,什么時候釋放這塊指針所指向的內存區域就成了一個問題。
智能指針就是解決這個問題的辦法,他的思想就是當你定義了一個智能指針,可以像普通指針一樣使用*??來獲取里面的內容,當用其他的智能指針再次指向這塊區域的時候,會有一個計數器。當所有的智能指針都被標記為不再使用的時候,這個計數器清零,這塊指針所指向的內存也就被釋放。
什么是動態分配的對象? 是指在程序運行時(而非編譯時)在堆(heap)內存中分配的對象。
注意
在使用智能指針的時候,一定要避免使用delete,可能會引發未定義的行為,就讓系統自己處理。
unique_ptr性能很好,不支持復制,唯一控制權;shared_ptr支持多個指針指向同一塊內存并計數,資源消耗大。
任何時候都推薦使用unique_ptr,而shared_ptr當多個函數
shared_ptr
引入#include <memory>
用法:shared _ptr<T> p = make_shared<T>()
或者大括號的初始方法shared_ptr<T> p {fp, close_file}
例如shared _ptr<int> p = make_shared<int>(100)
的意思就是定義了一個指向存儲內容為100的動態內存的共享指針p。此時p.use_count()
引用計數為1。
當定義其他的共享指針p1=p時,表示p1也指向了int類型的100的這塊內存,同理如果是對象,不會再構造一次,因為指向的是同一塊東西。此時再使用p.use_count()
或者p1.use_count()
得到的計數結果都是2。
當p.reset()
就表示重置p指向的內容,此時引用計數會減1,當p1.reset()
p1也重置之后,引用計數為0,這塊內存被釋放。
額外補充
p.reset()
這個用法還可以在括號里p.reset(new int)
代表指向一個新的int內存,舊的int內存減1- 在新定義一個共享指針的時候,是可以對它的釋放功能自定義的,可以自定義為其他的功能,例如下圖將釋放功能自定義為文件關閉函數。當引用計數為0的時候,就可以自動關閉文件。
- 別名
shared_ptr<example_class> p1 {p, &(p->bar)}
p1是p的別名
unique_ptr
引入#include <memory>
用法:unique_ptr<int> p = make_unique<int>(100)
unique_ptr就不存在復制這樣的功能,p獨享這一塊資源,不能和別的指針共享。
當作用域結束之后,unique_ptr指向的資源就會釋放,即使它是用的new delete來創建的。
可以使用p.get()
獲取裸指針。
同樣的,可以使用p.reset()
來重置unique_ptr使其指向nullptr。也可以p.reset(new class)
來讓這個unique_ptr指向一個新的類。
可以使用p.release()
來釋放p對這塊資源的控制權,會返回一個裸指針。unique_ptr中是沒有復制這么一說的,也就不存“=”的賦值。但是可以傳遞:通過unique<exapmle_class> p1 (p.release())
或者是利用move,即unique_ptr<example_class> p1(p.move())
unnique_ptr可以自定義分配函數和自定義釋放函數,eg:
unique_ptr的函數綁定是在編譯時就綁定了,這個函數成為了unique_ptr實例的一部分,而shared_ptr則是運行時綁定。
在函數之間的unique_ptr傳遞
指針在函數之間的傳遞難免不會發生復制,例如實參作為輸入進入函數之后,函數可能會復制一份進入函數體。
解決辦法:
- 函數的形參是一個unique_ptr的引用
- 只傳遞所指向的資源例如傳入*p,接收也是一個int& p。
- 利用p.get()或者是move(),這樣會傳遞一個裸指針到函數里去。
weak_ptr
weak_ptr是與shared_ptr成雙成對的,它只能作為一個觀察者。
對資源的引用是非擁有式的,因此不能控制資源的釋放。當想使用weak_ptr來煩我跟對象,需要使用shared_ptr<int> spt = wp.lock()
是會返回一個shared_ptr,然后就可以正常的使用*來訪問了