26.智能指针和动态内存
在C++中,動態內存的管理是通過一對運算符來完成的。new在動態內存中為對象分配空間并返回一個指向該內存對象的指針。
delete,接受一個動態對象的指針,銷毀該對象,并釋放與之關聯的內存!
為什么要用智能指針?
動態內存的使用很容易出現問題,因為確保在正確的時間釋放內存是極其困難的。有時候我們會忘記釋放內存這就會導致產生內存泄漏;為了更容易同時也更安全的使用動態內存,新的標準庫提供了兩種智能指針類型來管理動態對象!
智能指針分哪幾種?
shared_ptr : 允許多個指針指向一個對象
unique_ptr : 一個指針獨占一個對象
weak_ptr : 弱引用,執行shared_ptr指針指向的對象。(可參見 本博客C++模塊第9篇文章)
make_shared函數:在動態內存中創建一個對象并初始化它,返回指向該對象的一個shared_ptr指針。
#include<memory> shared_ptr<int> p2 = make_shared<int>(42);引用計數的問題:當用一個shared_ptr去初始化另一個shared_ptr,或者將它作為參數傳遞給一個函數,或者作為函數的返回值。這些操作都會增加引用計數!
一旦一個shared_ptr的計數器變為0,它就會自動釋放自己所管理的對象!
舉個栗子:
shared_ptr<FOO> factory(T arg) {return shared_ptr<FOO>(arg); }void use_factory(T arg) {shared_ptr<FOO> p = factory(arg); }有上面的代碼我們知道,當use_factory執行結束的時候,p就會被銷毀,由于p所指向的對象只有一個引用,那么當p被銷毀的時候,對應的對象的內存就會被釋放!
再來看,我們將代碼稍微變動一下
shared_ptr<FOO> factory(T arg) {return shared_ptr<FOO>(arg); }shared_ptr<FOO> use_factory(T arg) {shared_ptr<FOO> p = factory(arg);return p; }當use_factory執行結束的時候,p就會被銷毀,但是返回了p的副本,也就是增加了內存對象的引用計數,那么內存對象就不會被釋放!
new在動態內存中為對象分配空間并返回一個指向該內存對象的指針,該指針是內置類型指針,我們在使用完內存對象的時候一定要記得顯式地手動delete內存。
舉個栗子:
FOO* factory(T arg) {return new FOO(arg); }void use_factory(T arg) {FOO* p = factory(arg); }有上面的代碼我們知道,當use_factory執行結束的時候,p就會被銷毀,但是它所指向的內存沒有被釋放!這就會導致所謂的內存泄露!因此我們需要顯示的釋放內存!或者返回一個指針副本指向這個內存對象!
FOO* factory(T arg) {return new FOO(arg); }void use_factory(T arg) {FOO* p = factory(arg);delete p; }//或者void use_factory(T arg) {FOO* p = factory(arg);return p; }考慮到動態內存的管理很容易出現內存泄露或者把相同的指針delete多次。我們將new和shared_ptr結合使用!
但是new返回的是內置指針,因此只能夠在對shared_ptr進行初始化的時候,直接使用值初始化!而不能夠隱式轉換!
shared_ptr<int> p(new int(42));因此:一個用來初始化智能指針的普通指針必須是指向動態內存的!
那有沒有相反的機制?即從智能指針退化到普通指針?
答案當然是有!智能指針類型定義了一個get()函數,返回一個內置指針,它指向智能指針所指向的對象!記住!!!不能夠delete這個內置指針!因為智能指針還在引用這個內存對象!
?
當將一個內置指針所指向的內存綁定到一個智能指針上的時候,我們就不應該再使用內置指針去訪問這塊內存了。當我們需要向一個不能使用智能指針的代碼傳遞一個內置指針,就可以使用get函數。
get是用來將指針的訪問權限交給代碼,只有在確認代碼不會delete這個返回的內置指針的時候,才能夠使用get!而且不能使用get返回的指針去初始化另一個智能指針!
智能指針和異常
當我們使用某個代碼塊中使用了智能指針,且在代碼塊執行完成之前拋出了異常,且沒有進行捕獲處理,在程序退出之后,智能指針所指向的內存是可以被銷毀回收的!但是如果是內置的指針的話,那么在delete之前拋出異常導致程序退出的話,那么這部分內存無法被回收了,也即產生了內存泄露。
?
其它的shared_ptr的操作:
1)reset()
shared_ptr<int> p = make_shared<int>(42); p.reset(new int(42)); //p指向新的對象2)unique()? 用來判斷是否只有自身這一個智能指針指向這個內存對象!
if(!p.unique())p.reset(new string(*p)); //拷貝對象,指向新的拷貝對象! *P += “hello”; //如果是唯一的,改變對象的值unique_ptr 一個指針獨占一個對象!和new結合使用的時候,初始化必須要直接初始化!
由于unique_ptr獨享指向的內存對象!因此unique_ptr不支持普通的拷貝或賦值操作!
我們使用 release()和reset()將指針的所有權轉移到另一個unique_ptr上。
unique_ptr<int>p1(new int(1024)); unique_ptr<int>p2(p1.release()) unique_ptr<int>p3(new int(2048)); p2.reset(p3.release());?release()返回的內置指針常用來初始化另外一個智能指針或者給另外一個智能指針賦值!如果我們不用一個智能指針來保存返回的指針,我們就要記得手動delete這個指針!
還可以向unique_ptr傳遞刪除器
unique_ptr<objT,delT> p (new objT , fcn);weak_ptr:是一種不控制所指向對象的智能指針,它指向一個由shared_ptr管理的對象。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最后一個shared_ptr被銷毀,那么對象就會被釋放。即使有weak_ptr指向對象,對象也會被釋放!
在創建一個weak_ptr的時候,要用一個shared_ptr來初始化!
不能使用weak_ptr直接訪問對象,要使用lock()函數。此函數檢查weak_ptr指向的內存對象是否存在!如果存在返回一個shared_ptr。
shared_ptr<int> p = make_shared<int>(1024); weak_ptr<int>wp(p); if(shared<int> np = wp.lock()){} //存在則返回一個shared_ptrweak_ptr能夠解決空懸指針問題和循環引用的問題
?
總結
以上是生活随笔為你收集整理的26.智能指针和动态内存的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 25.C++:最通俗的讲解,什么是面向过
- 下一篇: 27.能否在构造函数中抛出异常?析构函数
