網站首頁 編程語言 正文
一、為啥使用智能指針呢
標準庫中的智能指針: std::auto_ptr --single ownership (C++98中出現,缺陷較多,被摒棄) std::unique_ptr --single ownership (C++11替代std::auto_ptr,用于單線程) std::shared_ptr --shared ownership (C++11,用于多線程) std::weak_ptr --temp/no ownership (C++11) Introduced in C++ 11 Defined inheader.
首先看一個下面的栗子,左邊是木有使用智能指針的情況,當執行foo()
函數,其中的e
指針會在bar(e)
時傳入bar
函數,但是在bar
函數結束后沒有人為delete e
時,就會導致內存泄漏;但是在右邊的栗子中,使用了unique_ptr
智能指針(single ownership),就能防止內存泄漏。
智能指針主要用于管理在堆上分配的內存,它將普通的指針封裝為一個棧對象。當棧對象的生存周期結束后,會在析構函數中釋放掉申請的內存,從而防止內存泄漏。
-
auto_ptr
智能指針:(C++11出來前只有這種智能指針)當對象拷貝或者賦值后,前面的對象就懸空了。 -
unique_ptr
智能指針:防止智能指針拷貝和復制。 -
shared_ptr
智能指針:通過引用計數的方式來實現多個shared_ptr
對象之間共享資源。 -
weak_ptr
智能指針:可以從一個shared_ptr
或另一個weak_ptr
對象構造,它的構造和析構不會引起引用記數的增加或減少。
注意:每一種智能指針都可以增加內存的引用計數。
- 智能指針分為兩類:
- 一種是可以使用多個智能指針管理同一塊內存區域,每增加一個智能指針,就會增加1次引用計數,
- 另一類是不能使用多個智能指針管理同一塊內存區域,通俗來說,當智能指針2來管理這一塊內存時,原先管理這一塊內存的智能指針1只能釋放對這一塊指針的所有權(ownership)。
- 按照這個分類標準,
auto_ptr
?unique_ptr
?weak_ptr
屬于后者,shared_ptr
屬于前者。
對shared_ptr
進行初始化時不能將一個普通指針直接賦值給智能指針,因為一個是指針,一個是類??梢酝ㄟ^make_shared
函數或者通過構造函數傳入普通指針。并可以通過get
函數獲得普通指針。
#include#include using namespace std; class report { private: string str; public: report(const string s):str(s) //構造方法 { cout<<"1 report Object has been build!"< ptr(new report(talk)); ptr->talk(); } { shared_ptr ptr(new report(talk)); ptr->talk(); } { unique_ptr ptr(new report(talk)); ptr->talk(); } return 0; }
二、shared_ptr智能指針
shared_ptr
實現了共享擁有的概念,利用“引用計數”來控制堆上對象的生命周期。
share_ptr
的生命周期:
原理:在初始化的時候引用計數設為1,每當被拷貝或者賦值的時候引用計數+1,析構的時候引用計數-1,直到引用計數被減到0,那么就可以delete掉對象的指針了。他的構造方式主要有以下三種:
shared_ptr
- 第一種空構造,沒有指定
shared_ptr
管理的堆上對象的指針,所以引用計數為0,后期可以通過reset()
成員函數來指定其管理的堆上對象的指針,reset()
之后引用計數設為1。 - 第二種是比較常見的構造方式,構造函數里面可以放堆上對象的指針,也可以放其他的智能指針(如
weak_ptr
)。 - 第三種構造方式指定了
shared_ptr
在析構自己所保存的堆上對象的指針時(即引用計數為0時)所要調用的函數,這說明我們可以自定義特定對象的特定析構方式。同樣的,reset()
成員函數也可以指定析構時調用的指定函數。 - 第四種方法:較常見,構造
shared_ptr
的方式(最安全):
auto ptr = make_shared(args);
上面第四種方法,使用標準庫里邊的
make_shared<>()
模板函數。該函數會調用模板類的構造方法,實例化一個堆上對象,然后將保存了該對象指針的shared_ptr
返回。參數是該類構造函數的參數,所以使用make_shared<>()
就好像單純地在構造該類對象一樣。auto
是C++11的一個關鍵字,可以在編譯期間自動推算變量的類型,在這里就是shared_ptr
類型。
shared_ptr
的其他成員函數:
use_count() //返回引用計數的個數 unique() //返回是否是獨占所有權(use_count是否為1) swap() //交換兩個shared_ptr對象(即交換所擁有的對象,引用計數也隨之交換) reset() //放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
三、unique_ptr智能指針
注意unique_ptr
是single ownership的,不能拷貝。其構造方式如下:
unique_ptr
的生命周期:
四、weak_ptr智能指針
五、智能指針怎么解決交叉引用,造成的內存泄漏
結論:創建對象時使用shared_ptr
強智能指針指向,其余情況都使用weak_ptr
弱智能指針指向。
5.1 交叉引用的栗子:
當A類中有一個指向B類的shared_ptr
強類型智能指針,B類中也有一個指向A類的shared_ptr
強類型智能指針。
main
函數執行后有兩個強智能指針指向了對象A,對象A的引用計數為2,B類也是:
#include#include using namespace std; class B; class A{ public: shared_ptr _bptr; }; class B{ public: shared_ptr _aptr; }; int main(){ shared_ptr aptr(new A()); shared_ptr bptr(new B()); aptr->_bptr = bptr; bptr->_aptr = aptr; return 0; }
而當主函數main
的return
返回后,對象A
的引用計數減一變為1(aptr
沒指向A
對象了),B
對象也是,引用計數不為0,即不能析構2個對象釋放內存,造成內存泄漏。
5.2 解決方案
將類A和類B中的shared_ptr
強智能指針都換成weak_ptr
弱智能指針;
class A{ public: weak_ptr _bptr; }; class B{ public: weak_ptr _aptr; };
weak_ptr
弱智能指針,雖然有引用計數,但實際上它并不增加計數,而是只觀察對象的引用計數。所以此時對象A的引用計數只為1,對象B的引用計數也只為1。
六、智能指針的注意事項
- 避免同一塊內存綁定到多個獨立創建的shared_ptr上,因此要不使用相同的內置指針初始化(或reset)多個智能指針,不要混合使用智能指針和普通指針,堅持只用智能指針。
- 不delete get() 函數返回的指針,因為這樣操作后,
shared_ptr
并不知道它管理的內存被釋放了,會造成shared_ptr重復析構。 - 不使用 get()函數初始化或(reset)另外的智能指針。
shared_ptrp = make_share (42); int *q = p.get(); { shared_ptr (q); } // 程序塊結束,q被銷毀,指向的內存被釋放。 int foo = *p; // 出錯,p指向的內存已經被q釋放,這是用get() 初始化另外的智能指針惹得禍。 // 請記住,永遠不要用get初始化另外一個智能指針。
能使用unique_ptr
時就不要使用share_ptr
指針(后者需要保證線程安全,所以在賦值or銷毀時overhead開銷更高)。
總結
原文鏈接:https://blog.csdn.net/qq_35812205/article/details/123270095
相關推薦
- 2022-06-01 Kubernetes集群的組成介紹_云和虛擬化
- 2022-04-02 python3?QT5?端口轉發工具兩種場景分析_python
- 2022-11-23 Python利用keyboard模塊實現鍵盤記錄操作_python
- 2022-09-29 利用LyScript實現應用層鉤子掃描器_python
- 2022-12-06 C++中調用復制(拷貝)函數的三種情況總結_C 語言
- 2023-07-13 websocket的使用及nginx通信的ws代理配置
- 2023-01-10 Qt實現可以計算大數的簡單計算器_C 語言
- 2022-04-28 WPF使用DockPanel??棵姘宀季謃實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支