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