網(wǎng)站首頁 編程語言 正文
智能指針模板類
void remodel(std::string & str) { std::string *ps =new std::string(str); .... if(oh_no) throw exception(); ... delete ps; return; }
如果上面這段函數(shù)出現(xiàn)異常,那么就會發(fā)生內(nèi)存泄漏。傳統(tǒng)指針在執(zhí)行動(dòng)態(tài)內(nèi)存分配時(shí)具有缺陷,很容易導(dǎo)致內(nèi)存泄漏。如果有一個(gè)指針,指針被釋放的同時(shí),它指向的內(nèi)存也能被釋放,那就完美了。這種指針就是智能指針。
我們只介紹3個(gè)智能指針模板類:auto_ptr
、unique_ptr
、shared_ptr
,順便會提一下weak_ptr
。其中auto_ptr
已經(jīng)被拋棄了,它是一個(gè)過時(shí)的產(chǎn)物,我們介紹它只為拋磚引玉。
使用智能指針
這些智能指針模板定義了類似指針的對象,我們把new
獲得的地址賦給這種對象,當(dāng)智能指針過期時(shí),它的析構(gòu)函數(shù)會自動(dòng)釋放動(dòng)態(tài)內(nèi)存。
必須包含頭文件memory
,這個(gè)文件包含模板定義。我們使用模板實(shí)例化來創(chuàng)建所需指針.
類模板大概是:
template<class X> class auto_ptr { public: explicit auto_ptr(X* p) noexcept; .... }
所以我們實(shí)例化:
auto_ptr<double> pd(new double);
或者auto_ptr<string> ps(new string);
。
對于其他兩種智能指針也是一樣的構(gòu)造語法。
//智能指針1.cpp #include<iostream> #include<string> #include<memory> class Report { private: std::string str; public: Report(const std::string &s):str(s){std::cout<<"Object created!\n";} ~Report(){std::cout<<"Object deleted";} void comment() const{std::cout<<str<<"\n";} }; int main() { { std::auto_ptr<Report> ps(new Report("using auto_ptr")); ps->comment(); } { std::unique_ptr<Report> ps(new Report("using unique_ptr")); ps->comment(); } { std::shared_ptr<Report> ps(new Report("using shared_ptr")); ps->comment(); } }
Object created!
using auto_ptr
Object deletedObject created!
using unique_ptr
Object deletedObject created!
using shared_ptr
Object deleted
注意,所有智能指針類都有一個(gè)explicit
構(gòu)造函數(shù),該構(gòu)造函數(shù)將指針作為參數(shù)。所以它不會將指針轉(zhuǎn)換成智能指針對象。
shared_ptr<double> pd; double *p_reg=new double; pd=p_reg;//不允許因?yàn)闃?gòu)造函數(shù)是`explicit`修飾的,所以不能隱式類型轉(zhuǎn)換 pd=shared_ptr<double>(p_reg);//允許,使用賦值運(yùn)算符 shared_ptr<double> pshared=p_reg;//不允許,因?yàn)椴荒茈[式類型轉(zhuǎn)換 shared_ptr<double> pshared(p_reg);//允許調(diào)用構(gòu)造函數(shù)。
智能指針和傳統(tǒng)指針有很多類似的語法:例如可以使用*ps
來解除引用,也可以使用ps->
來訪問結(jié)構(gòu)成員。
但是最重要的不同是:我們只能用能進(jìn)行delete
或者delete[]
的指針來構(gòu)造智能指針。
也就是說:
int a=5; int *p=&a; shared_ptr<int> ps(p);
上面這段代碼就是錯(cuò)誤的,因?yàn)?code>p無法使用delete
.
#include<memory> #include<iostream> int main() { using namespace std; /*{ auto_ptr<int[]> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; }*/ { unique_ptr<int[]> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; } { shared_ptr<int []> ps(new int[2]{1,2}); cout<<ps[0]<<endl; cout<<ps[1]<<endl; } }
上面代碼告訴我們,我們可以使用<int []>
這樣實(shí)例化模板,這是為了模擬動(dòng)態(tài)數(shù)組。
關(guān)于智能指針的注意事項(xiàng)
auto_ptr<string> ps(new string("I reigned lonely as a cloud.")); auto_ptr<string> pd; pd=ps;
上面這段代碼不會報(bào)錯(cuò),但是你可能會問:pd和ps都是智能指針,如果我們把ps賦給pd;那么就說明這兩個(gè)指針指向同一個(gè)string
對象,那么當(dāng)這兩個(gè)指針消失時(shí),會對同一個(gè)string
對象釋放兩次?
我們看看我們?nèi)绾伪苊膺@種問題:
- 進(jìn)行深復(fù)制
- 建立所有權(quán)概念,對于特定的對象,只能有一個(gè)智能指針擁有它,這樣就只有擁有它的智能指針的析構(gòu)函數(shù)才會釋放內(nèi)存。然后賦值運(yùn)算會轉(zhuǎn)讓所有權(quán)。
auto_ptr
和unique_ptr
都是使用這種策略,但是unique_ptr
更嚴(yán)格。 - 使用引用計(jì)數(shù),每個(gè)對象都會記錄有多少個(gè)智能指針指向它,然后賦值運(yùn)算時(shí),計(jì)數(shù)加1,指針過期時(shí)計(jì)數(shù)減1。當(dāng)最后一個(gè)指針過期時(shí),才會調(diào)用
delete
,這是shared_ptr
的策略。
實(shí)際上,上述這些策略也適用于復(fù)制構(gòu)造函數(shù)。
實(shí)際上,unique_ptr
就是"唯一指針",指針和被指向的對象一一對應(yīng),而shared_ptr
就是"分享指針",它允許多個(gè)指針指向同一個(gè)對象。所以說,shared_ptr
的用法更像C風(fēng)格指針。
我們看上面的代碼,pd=ps
后,由于string
對象的所有權(quán)交給了pd
,所以*ps
就無法使用了。
//智能指針3.cpp #include<iostream> #include<string> #include<memory> int main() { using namespace std; auto_ptr<string> films[5]= { auto_ptr<string>(new string("1")), auto_ptr<string>(new string("2")), auto_ptr<string>(new string("3")), auto_ptr<string>(new string("4")), auto_ptr<string>(new string("5")) }; auto_ptr<string> p; p=films[2]; for(int i=0;i<5;i++) { cout<<*films[i]<<endl; } cout<<*p<<endl; }
上面這段代碼會出錯(cuò),因?yàn)?code>p=films[2];使得,films[2]
的所有權(quán)轉(zhuǎn)讓給p
了,所以cout<<*film[2]
就會出錯(cuò)。但是如果使用shared_ptr
代替auto_ptr
就可以正常運(yùn)行了。如果使用unique_ptr
呢?程序會在編譯階段報(bào)錯(cuò),而不是在運(yùn)行階段報(bào)錯(cuò),所以說unique_ptr
更加嚴(yán)格。
unique_ptr優(yōu)于auto_ptr
首先就是上面談過的,unique_ptr
的所有權(quán)概念比auto_ptr
要嚴(yán)格,所以unique_ptr
更加安全。
unique_ptr<string> ps(new string("I reigned lonely as a cloud.")); unique_ptr<string> pd; pd=ps;
上述代碼會在編譯階段報(bào)錯(cuò),因?yàn)槌霈F(xiàn)了危險(xiǎn)的懸掛指針ps
(即野指針,指針指向被刪除的內(nèi)存,如果使用野指針修改內(nèi)存是會造成嚴(yán)重后果)。
但是有時(shí)候?qū)⒁粋€(gè)智能指針賦給另一個(gè)并不會留下懸掛指針:
unique_ptr<string> demo(const char*s) { unique_ptr<string> temp(new string(s)); return temp; } ... unique_ptr<string>ps; ps= demo("something"); ...
demo()
函數(shù)返回一個(gè)臨時(shí)變量temp
,然后臨時(shí)變量temp
被賦給ps
,那么temp
就變成懸掛指針了,但是我們知道ps=demo("something")
一旦運(yùn)行結(jié)束,demo()
里的所有局部變量都會消失包括temp
。所以即使temp
是野指針,我們也不會使用它。神奇的是,編譯器也允許上面這種賦值。
總之,程序試圖將一個(gè)unique_ptr
賦給另一個(gè)時(shí),如果源unique_ptr
是個(gè)臨時(shí)右值,編譯器允許這么做;如果源unique_ptr
會存在一段世界,編譯器禁止這么做。
unique_ptr<string> pu1; pu1=unique_ptr<string>(new string("yo!"));
上面這段代碼也是允許的,因?yàn)?code>unique_ptr<string>(new string("yo!"))是一個(gè)臨時(shí)右值(右值都是臨時(shí)的,右值只在當(dāng)前語句有效,語句結(jié)束后右值就會消失)
unique_ptr<string> ps(new string("I reigned lonely as a cloud.")); unique_ptr<string> pd; pd=std::move(ps);
上面代碼是正確的,如果你想要進(jìn)行將unique_ptr
左值,賦給unique_ptr
左值,那么你必須使用move()
函數(shù),這個(gè)函數(shù)會將左值轉(zhuǎn)換成右值。
以上所說,反映了一個(gè)事實(shí):unique_ptr
比auto_ptr
安全。其實(shí)unique_ptr
還有一個(gè)優(yōu)點(diǎn):auto_ptr
的析構(gòu)函數(shù)只能使用delete
,而unique_ptr
的析構(gòu)函數(shù)可以使用delete[]
和delete
。
選擇智能指針
首先明確一個(gè)事實(shí):shared_ptr
更方便;unique_ptr
更安全。
如果程序需要適用多個(gè)指向同一個(gè)對象的指針,那么只能選擇shared_ptr
;如果不需要多個(gè)指向同一個(gè)對象的指針,那么兩種指針都可以使用。總之,嫌麻煩的話就全部用shared_ptr
.
#include<memory> #include<iostream> #include<vector> #include<cstdlib> #include<algorithm> std::unique_ptr<int> make_int(int n) { return std::unique_ptr<int>(new int(n)); } void show(const std::unique_ptr<int> &pi) { std::cout<<*pi<<' '; } int main() { using std::vector; using std::unique_ptr; using std::rand; int size=10; vector<unique_ptr<int>> vp(size); for(int i=0;i<size;i++) vp[i]=make_int(rand()%1000);//#1 vp.push_back(make_int(rand()%1000));//#2 std::for_each(vp.begin(),vp.end(),show);//#3 }
上面這段代碼是使用unique_ptr
寫的,#1.#2是沒有問題的,因?yàn)楹瘮?shù)返回值是臨時(shí)右值,#3就要注意了.show()
函數(shù)使用的是引用參數(shù),如果換成按值傳遞,那就會出錯(cuò),因?yàn)檫@會導(dǎo)致,使用unique_ptr
左值初始化pi
,這時(shí)不允許的,記得嗎?在使用unique_ptr
時(shí),它的賦值運(yùn)算符要求:只能用右值賦給左值。(實(shí)際上,它的復(fù)制構(gòu)造函數(shù)也要求只接受右值)。
當(dāng)unique_ptr
是右值的時(shí)候,我們可以把他賦給shared_ptr
。
shared_ptr
包含一個(gè)顯式構(gòu)造函數(shù),他會把右值unique_ptr
轉(zhuǎn)換成shared_ptr
:
unique_ptr<int> pup(make_int(rand()%1000));//ok shared_ptr<int> spp(pup);//不允許,構(gòu)造函數(shù)不能接受`unique_ptr`的左值 shared_ptr<int> spr(make_int(rand()%1000));//ok
weak_ptr
weak_ptr
正如它名字所言:一個(gè)虛弱的指針,一個(gè)不像是指的指針。weak_ptr
是用來輔助shared_ptr
的。
為什么說weak_ptr
不像指針呢?是因?yàn)樗鼪]有重載*
和[]
運(yùn)算符。
通常,我們使用shared_ptr
來初始化weak_ptr
,那么這兩個(gè)指針都指向是同一塊動(dòng)態(tài)內(nèi)存。
weak_ptr
是shared_ptr
的輔助,所以它幫忙能查看這塊動(dòng)態(tài)內(nèi)存的信息:包括引用計(jì)數(shù)、存的信息。
#include<memory> #include<iostream> int main() { using std::shared_ptr; using std::weak_ptr; using std::cout; using std::endl; shared_ptr<int> p1(new int(255)); weak_ptr<int>wp(p1); cout<<"引用計(jì)數(shù): "<<wp.use_count()<<endl; cout<<"存儲信息: "<<*(wp.lock())<<endl; shared_ptr<int> p2=p1; cout<<"引用計(jì)數(shù): "<<wp.use_count()<<endl; cout<<"存儲信息: "<<*(wp.lock())<<endl; }
引用計(jì)數(shù): 1 ?
存儲信息: 255
引用計(jì)數(shù): 2 ?
存儲信息: 255
weak_ptr
的類方法中use_count()
查看指向和當(dāng)前weak_ptr
指針相同的shared_ptr
指針的數(shù)量,lock()
函數(shù)返回一個(gè)和當(dāng)前weak_ptr
指針指向相同的shared_ptr
指針。
原文鏈接:https://blog.csdn.net/m0_71009069/article/details/126410117
相關(guān)推薦
- 2022-03-23 C++?Boost?PropertyTree解析INI文件詳解_C 語言
- 2022-07-14 React父子組件傳值(組件通信)的實(shí)現(xiàn)方法_React
- 2022-04-20 C++的數(shù)據(jù)類型你真的了解嗎_C 語言
- 2023-11-14 docker啟動(dòng)報(bào)錯(cuò):Job for docker. service failed because
- 2022-09-05 Redis的數(shù)據(jù)刪除策略
- 2022-10-05 Redis實(shí)現(xiàn)數(shù)據(jù)的交集、并集、補(bǔ)集的示例_Redis
- 2023-02-02 C++調(diào)用matlab引擎實(shí)現(xiàn)三維圖的繪制_C 語言
- 2022-10-03 React在定時(shí)器中無法獲取狀態(tài)最新值的問題_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支