智能指针的相关讲解
文章目錄
- 1.new和delete操作符
- 1)new運(yùn)算符做了兩件事
- 2)delete也做了兩件事
- 3)補(bǔ)充:
- 2.shared_ptr
- 1)概念
- 2)一般形式
- 3)常規(guī)初始化(shared_ptr和new配合使用)
- 4)make_shared函數(shù)
- 5)shared_ptr引用計(jì)數(shù)的增加和減少
- 6)shared_ptr指針常用操作
- 1.use_count函數(shù)
- 2.unique成員函數(shù)
- 3.reset成員函數(shù)
- 4.*解引用
- 5.get成員函數(shù)
- 6.swap成員函數(shù)
- 7.=nullptr
- 8.智能指針名字作為判斷條件
- 9.指定刪除器和數(shù)組問(wèn)題
- 3.weak_ptr
- 1)簡(jiǎn)介
- 2)常用操作
- 1.use_count函數(shù)
- 2.expired函數(shù)
- 3.reset函數(shù)
- 4.lock函數(shù)
- 3)尺寸問(wèn)題
- 4.shared_ptr使用場(chǎng)景、陷阱、性能分析與使用建議
- 1)std::shared_ptr使用場(chǎng)景
- 2)std::shared_ptr使用陷阱分析
- 1.慎用裸指針
- 2.慎用get返回的指針
- 3.用enable_shared_from_this返回this
- 4.避免循環(huán)引用
- 3)性能分析
- 1.尺寸問(wèn)題
- 2.移動(dòng)語(yǔ)義
- 4)補(bǔ)充說(shuō)明和使用建議
- 5.unique_ptr簡(jiǎn)介與常用操作
- 1)unique_ptr簡(jiǎn)介
- 2)unique_ptr常用操作
- 1.unique_ptr不支持的操作
- 2.移動(dòng)語(yǔ)義
- 3.release成員函數(shù)
- 4.reset成員函數(shù)
- 5.=nullptr
- 6.指向一個(gè)數(shù)組
- 7.get成員函數(shù)
- 8.*解引用
- 9.swap成員函數(shù)
- 10.智能指針名字作為判斷條件
- 11.轉(zhuǎn)換成shared_ptr類(lèi)型
- 3)返回unique_ptr
- 4)刪除器
- 5)尺寸問(wèn)題
- 6.智能指針總結(jié)
- 1)設(shè)計(jì)思想
- 2)auto_ptr為什么被廢棄
- 3)智能指針的選擇
1.new和delete操作符
1)new運(yùn)算符做了兩件事
- ①分配內(nèi)存(new就是通過(guò)operate new來(lái)分配內(nèi)存的)
- ②調(diào)用構(gòu)造函數(shù)初始化該內(nèi)存
2)delete也做了兩件事
- ①調(diào)用析構(gòu)函數(shù)
- ②釋放內(nèi)存(delete就是通過(guò)operate delete()來(lái)釋放內(nèi)存的)
3)補(bǔ)充:
delete [ ]pA中,C++會(huì)多分配4字節(jié)的大小專(zhuān)門(mén)專(zhuān)門(mén)保存數(shù)組的大小,在delete [ ] 時(shí)就可以去除這個(gè)數(shù)組大小的數(shù)字,就知道了需要調(diào)用析構(gòu)函數(shù)多少次
2.shared_ptr
1)概念
- 共享指針,多個(gè)指針指向同一個(gè)對(duì)象,最后一個(gè)指針被銷(xiāo)毀時(shí),這個(gè)對(duì)象就會(huì)被釋放
2)一般形式
- shared_ptr<指向的類(lèi)型>智能指針名
- shared_ptr<string.>p1; //這是一個(gè)指向string的智能指針,名字為p1
3)常規(guī)初始化(shared_ptr和new配合使用)
①常規(guī)
shared_ptr<int>pi(new int(100)); //pi指向一個(gè)值為100的int數(shù)據(jù) shared_ptr<int>pi2 = new int(100);//這個(gè)寫(xiě)法不行,智能指針必須是explicit,是不可以進(jìn)行隱式類(lèi)型轉(zhuǎn)換的,必須用直接初始化方式,而待等號(hào)一般都要表示隱式類(lèi)型轉(zhuǎn)換②對(duì)于返回值為shared_ptr<int.>類(lèi)型,看看下面的范例:
shared_ptr<int> makes(int value) {return new int(value);//不可以,因?yàn)闊o(wú)法把new得到的int*換成shared_ptr }所以要修改為
shared_ptr<int>makes(int value) {return shared_ptr<int>(new int(value));//可以,顯示使用int*創(chuàng)建shared_ptr<int> }③裸指針可以用來(lái)初始化shared_ptr,但是這是一種不被推薦的用法,穿插使用容易出問(wèn)題,盡量使用后面會(huì)講到的make_shared
int *pi = new int; shared_ptr<int> p1(pi);上面的寫(xiě)法不推薦,應(yīng)該直接傳遞new運(yùn)算符,而不是一個(gè)裸指針變量
shared_ptr<int>p1(new int);4)make_shared函數(shù)
- 簡(jiǎn)介:被認(rèn)為是最安全和高效的分配和使用shared_ptr智能指針的模板,能在動(dòng)態(tài)內(nèi)存(堆)中分配并初始化一個(gè)對(duì)象,然后返回指向此對(duì)象的shared_ptr
- make_shared使用起來(lái)雖然不錯(cuò),后面還提到自定義刪除器,如果使用make_shared方法生成shared_ptr對(duì)象,那就沒(méi)有辦法自定義刪除器了
5)shared_ptr引用計(jì)數(shù)的增加和減少
1.引用計(jì)數(shù)的增加
每個(gè)shared_ptr都會(huì)記錄有多少個(gè)其他shared_ptr指向相同的對(duì)象
(1)像下面的代碼這樣,p6初始化p7,就會(huì)導(dǎo)致所有指向該對(duì)象(內(nèi)存)的shared_ptr引用計(jì)數(shù)全部增加1
(2)把引用計(jì)數(shù)當(dāng)成實(shí)參往函數(shù)里面?zhèn)鬟f
void myfunc(shared<int>&ptmp)//傳遞引用作為形參,則引用計(jì)數(shù)不會(huì)增加 {return ptmp; }在main主函數(shù)中,繼續(xù)增加如下代碼
myfunc(p7);//這個(gè)函數(shù)執(zhí)行后,這個(gè)指針的引用計(jì)數(shù)會(huì)恢復(fù)(3)作為函數(shù)的返回值
shared_ptr<int>myfunc2(shared_ptr<int>&ptmp)//這里是引用,所以計(jì)數(shù)還是2 {return ptmp; }在主函數(shù)中增加以下代碼
auto p8 = myfunc2(p7);//p8接受myfunc2函數(shù)返回值,那么此時(shí)引用計(jì)數(shù)會(huì)變成32.引用計(jì)數(shù)的減少
(1)給shared_ptr賦一個(gè)新值,讓該shared_ptr指向一個(gè)新對(duì)象,在main主函數(shù)中增加以下代碼
(2)局部的shared_ptr離開(kāi)作用域
auto p6 = std::make_shared<int>(100); auto p7(p6);// myfunc(p7);//進(jìn)入函數(shù)體myfunc中時(shí)有3個(gè)引用計(jì)數(shù),從myfunc中返回時(shí)引用計(jì)數(shù)恢復(fù)為2(3)當(dāng)一個(gè)shared_ptr引用計(jì)數(shù)為0,他會(huì)Zion給釋放自己所管理的對(duì)象
auto p9 = std::make_shared<int>(100);//只有p9指向該對(duì)象 auto p10 = std::make_shared<int>(100); p9 = p10;//p9指向p10的對(duì)象,該對(duì)象引用計(jì)數(shù)為2,而原來(lái)p9指向的對(duì)象引用計(jì)數(shù)會(huì)變?yōu)?,所以會(huì)被自動(dòng)釋放6)shared_ptr指針常用操作
1.use_count函數(shù)
用于返回多少個(gè)智能指針指向某個(gè)對(duì)象
shared_ptr<int>myp(new int(100)); int icount = myp.use_count();//1 shared_ptr<int>myp2(myp); icount = myp2.use_count();//22.unique成員函數(shù)
是否該智能指針獨(dú)占某個(gè)指向的對(duì)象,,也就是若只有一個(gè)智能指針指向某個(gè)對(duì)象,則unique返回true,否則返回false
shared_ptr<int>myp(new int(100)); if(myp.unique()) //本條件成立 {cout<<"myp unique ok"<<endl; } shared_ptr<int>myp2(myp); if(myp.unique()) {cout<<"myp unique ok"<<endl; }3.reset成員函數(shù)
(1)當(dāng)reset不帶參數(shù)時(shí)
當(dāng)pi是唯一指向該對(duì)象的指針,則釋放pi所指向的對(duì)象,將pi置空
若pi不是唯一指向該對(duì)象的指針,則不釋放pi所指向的對(duì)象,但指向該對(duì)象引用計(jì)數(shù)會(huì)減1,同時(shí)將pi置空
繼續(xù)演示若pi不是唯一指向該對(duì)象的指針的情形
shared_ptr<int>(new int(100)); auto pi2(pi); //pi2引用計(jì)數(shù)現(xiàn)在為2 pi.reset(); //pi被置空,pi2引用計(jì)數(shù)變?yōu)?(2)當(dāng)reset帶參數(shù)(一般是一個(gè)new出來(lái)的指針)時(shí)
若pi是唯一指向該對(duì)象的指針,則釋放pi所指向的對(duì)象,讓pi指向新內(nèi)存
若pi不是唯一指向該對(duì)象的指針,則不釋放pi指向?qū)ο?#xff0c;但是指向該對(duì)象的引用計(jì)數(shù)會(huì)減1,同時(shí)讓pi指向新內(nèi)存
演示若pi不是唯一指向該對(duì)象的指針的情形
shared_ptr<int>pi(new int(100)); auto pi2(pi); pi.reset(new int(1));//現(xiàn)在pi引用計(jì)數(shù)為1,pi2引用計(jì)數(shù)也為1 if(pi.unique())//本條件成立 {cout<<"pi unique ok"<<endl; }(3)空指針也可以通過(guò)reset來(lái)重新初始化
shared_ptr<int>p; p.reset(new int(100));//p指向新內(nèi)存4.*解引用
獲得p指向的對(duì)象
shared_ptr<int>pother(new int(12345)); char outbuf[1024]; sprintf_s(outbuf,sizeof(outbuf),"%d",*pother);//outbuf中的內(nèi)容就是12345,pother不發(fā)生變化,引用計(jì)數(shù)仍舊為1 OutputDebugStringA(outbuf);//在MyProjectMFC工程中使用F5運(yùn)行,執(zhí)行到這行可以打印輸出outbuf的內(nèi)容5.get成員函數(shù)
p.get()返回p中保存的指針
小心使用,若智能指針釋放了所指向的對(duì)象,則返回的這個(gè)指針?biāo)赶虻膶?duì)象就變得無(wú)效了
6.swap成員函數(shù)
交換兩個(gè)智能指針?biāo)赶虻膶?duì)象
shared_ptr<string>ps1(new string("I love china1!")); shared_ptr<string>ps2(new string("I love china2!")); std::swap(ps1,ps2);//可以這么操作 ps1.swap(ps2);//也可以這么操作7.=nullptr
- 將指針指向的引用計(jì)數(shù)減1,若引用計(jì)數(shù)變?yōu)?,則釋放智能指針?biāo)赶虻膶?duì)象
- 將智能指針置空
8.智能指針名字作為判斷條件
shared_ptr<string> ps1(new string("I love china!")); //若ps1指向一個(gè)對(duì)象,則條件成立 if(ps1)//條件成立 {cout<<"ps1"<<endl;//執(zhí)行 }9.指定刪除器和數(shù)組問(wèn)題
1)指定刪除器
可以為智能指針定義自己的寫(xiě)的刪除器
在main主函數(shù)中,加入如下代碼:
shared_ptr<int>p(new int(12345),myDeleter);//指定刪除器 shared_ptr<int>p2(p); p2.reset();//p2為nullptr了 p.reset();//調(diào)用自己的刪除器,釋放鎖指向的對(duì)象,同時(shí)p置空lamdba表達(dá)式也可以定義刪除器
shared_ptr<int>p(new int(12345),[](int*p) {delete p; } p.reset();//會(huì)帶哦用刪除器(lamdba表達(dá)式)為什么要自己定義刪除器?
當(dāng)默認(rèn)的刪除器處理不了——用shared_ptr管理動(dòng)態(tài)數(shù)組的時(shí)候,需要自己指定自己的刪除器,默認(rèn)的刪除器不支持?jǐn)?shù)組對(duì)象
shared_ptr<int[]>p(new int[10],[](int*p)) {delete[]p; }); p.reset();如果一個(gè)類(lèi)中帶有析構(gòu)函數(shù),那么必須定義自己的刪除器,否則會(huì)報(bào)異常
class A { public:A(){cout<<""<<endl;}~A(){cout<<""<<endl;} };在main主函數(shù)中加入如下代碼
shared_ptr<A>pA(new A[10),[](A*p) {delete[]p; }); //還可以這么寫(xiě) shared_ptr<A>pA(new A[10],std::default_delete<A[]>()); //不寫(xiě)刪除器,也可以這么定義 shared_ptr<A[]>pA(new A[]);//<>中加個(gè)[]就行 shared_ptr<int[]>p(new in[10]);2)指定刪除器的額外說(shuō)明
略
3.weak_ptr
1)簡(jiǎn)介
- 用來(lái)輔助shared_ptr工作的
- 將weak_ptr綁定到shared_ptr并不會(huì)改變shared_ptr的引用計(jì)數(shù)(更確切的說(shuō),weak_ptr的構(gòu)造和析構(gòu)函數(shù)不會(huì)增加或減少鎖指向?qū)ο蟮囊糜?jì)數(shù))
- weak_ptr的創(chuàng)建一般用make_shared來(lái)初始化
- 程序員不能通過(guò)weak_ptr直接訪(fǎng)問(wèn)對(duì)象的,必須要用一個(gè)lock的成員函數(shù),lock的功能就是檢查weak_ptr鎖指向的對(duì)象是否還存在,如果存在,lock能夠返回一個(gè)空的shared_ptr
- weak_ptr具備能夠判斷所指向的對(duì)象是否存在的能力
2)常用操作
1.use_count函數(shù)
auto pi = make_shared<int>(100); auto pi2(pi);//pi2類(lèi)型是一個(gè)shared_ptr weak_ptr<int>piw(pi); int isc = piw.use_count();2.expired函數(shù)
- 是否過(guò)期的意思,弱該指針的use_count為0,則返回true,否則返回false
3.reset函數(shù)
- 將該弱引用指針設(shè)置為空,不影響指向該對(duì)象的強(qiáng)引用數(shù)量,但指向該對(duì)象的弱引用數(shù)量會(huì)減1
4.lock函數(shù)
- 獲得監(jiān)視的shared_ptr,下面是完整的演示
- 上面的代碼改造以下,看如下這個(gè)比較完整的演示,引入一個(gè){ }
3)尺寸問(wèn)題
- weak_ptr的尺寸是裸指針的2倍,其他略
4.shared_ptr使用場(chǎng)景、陷阱、性能分析與使用建議
1)std::shared_ptr使用場(chǎng)景
shared_ptr<int>create0(int value) {return make_shared<int>(value);//返回一個(gè)shared_ptr } void myfunc(int value) {shared_ptr<int>ptmp = create0(10);return;//ptmp離開(kāi)了作用域(ptmp是局部變量),因此他指向的內(nèi)存會(huì)被自動(dòng)釋放 }- 在主函數(shù)中,加入如下代碼
myfunc(12); - 現(xiàn)在改造以下myfunc函數(shù)
2)std::shared_ptr使用陷阱分析
1.慎用裸指針
- 如果把一個(gè)普通裸指針綁定到了一個(gè)shared_ptr,那么內(nèi)存管理的責(zé)任就交給智能指針,就不應(yīng)該再使用裸指針(內(nèi)置指針)訪(fǎng)問(wèn)shared_ptr指定的內(nèi)存了
- 但是不要用裸指針初始化多個(gè)shared_ptr對(duì)象,兩個(gè)指針無(wú)關(guān)聯(lián)關(guān)系,釋放裸指針?biāo)赶虻膬?nèi)存要釋放2次,這顯然會(huì)出問(wèn)題
修改為
//可修改為 shared_ptr<int>p1(new int);//大大降低了用pi來(lái)創(chuàng)建p2的可能性2.慎用get返回的指針
- get返回的指針不能delete,否則會(huì)產(chǎn)生異常,也不能將其他智能指針綁到get返回的指針上
3.用enable_shared_from_this返回this
- 看如下代碼
- 在main主函數(shù)里面,加入如下代碼
- 上面的代碼用同一個(gè)指針構(gòu)造了兩個(gè)智能指針pct1和pct2,兩個(gè)之怎能指針之間沒(méi)有任何關(guān)系,也就是釋放同一個(gè)都西昂內(nèi)存會(huì)釋放兩次,解決方法如下
4.避免循環(huán)引用
- 循環(huán)引用會(huì)導(dǎo)致內(nèi)存泄漏
- 解決辦法:把其中一個(gè)shared_ptr寫(xiě)成weak_ptr
3)性能分析
1.尺寸問(wèn)題
尺寸是裸指針的2倍
2.移動(dòng)語(yǔ)義
- 全程引用計(jì)數(shù)為1
4)補(bǔ)充說(shuō)明和使用建議
make_shared比普通指針的智能效率高,只分配一次內(nèi)存
shared_ptr<string>ps1(new string("I love China!"));//這句話(huà)至少分配兩次內(nèi)存5.unique_ptr簡(jiǎn)介與常用操作
1)unique_ptr簡(jiǎn)介
- 獨(dú)占式智能指針
- unique的一般形式
1.常規(guī)初始化
unique_ptr<int>pi2(new int(102));2.make_unique函數(shù)
- C++11不支持,C++14才有
2)unique_ptr常用操作
1.unique_ptr不支持的操作
- 不支持復(fù)制和賦值
2.移動(dòng)語(yǔ)義
- 支持移動(dòng)語(yǔ)義
3.release成員函數(shù)
- 放棄對(duì)指針的控制權(quán),返回裸指針,將智能指針?lè)趴铡7祷氐穆阒羔樋梢允止elete釋放,也可以用來(lái)初始化另外一個(gè)智能指針,或者給另外一個(gè)智能指針賦值
4.reset成員函數(shù)
和shared_ptr一樣
5.=nullptr
和shared_ptr一樣
6.指向一個(gè)數(shù)組
std::unique_ptr<int[]>ptrarray(new int[10]); ptrarray[0] = 12;//數(shù)組提供索引運(yùn)算符[] ptrarray[2] = 9;7.get成員函數(shù)
和shared_ptr一樣
8.*解引用
- 數(shù)組是沒(méi)有*解引用運(yùn)算符的
9.swap成員函數(shù)
和shared_ptr一樣
10.智能指針名字作為判斷條件
若ps1指向一個(gè)對(duì)象,則不為空
11.轉(zhuǎn)換成shared_ptr類(lèi)型
unique_ptr<std::string>ps(new std::string("I love China!")); shared_ptr<string>ps2 = std::move(ps);3)返回unique_ptr
- 生成局部對(duì)象的unique_ptr可以返回復(fù)制,因?yàn)橐讳N(xiāo)毀了
4)刪除器
1.指定刪除器
1)范例
2.補(bǔ)充指定刪除器
shared_ptr的刪除器更靈活,相同類(lèi)型就可以共用刪除器,但是unique_ptr的刪除器不一樣,不靈活
5)尺寸問(wèn)題
通常情況下unique_ptr的尺寸和裸指針一樣,若刪除器是一個(gè)函數(shù),unique_ptr的尺寸就會(huì)發(fā)生變化
6.智能指針總結(jié)
1)設(shè)計(jì)思想
防止忘記內(nèi)存釋放,造成內(nèi)存泄漏
2)auto_ptr為什么被廢棄
不能在容器中保存auto_ptr,也不能從函數(shù)中返回auto_ptr,已經(jīng)被unique_ptr取代
3)智能指針的選擇
優(yōu)先考慮unique_ptr,要使用多個(gè)指向同一個(gè)對(duì)象的指針的話(huà)用shared_ptr
總結(jié)
- 上一篇: cv2.imshow无法正常显示图片,而
- 下一篇: OCCT里的Mesh网格计算流程