标准库中的智能指针shared_ptr
智能指針的出現(xiàn)是為了能夠更加方便的解決動(dòng)態(tài)內(nèi)存的管理問(wèn)題。注:曾經(jīng)記得有本書(shū)上說(shuō)可以通過(guò)vector來(lái)實(shí)現(xiàn)動(dòng)態(tài)分配的內(nèi)存的自動(dòng)管理,但是經(jīng)過(guò)試驗(yàn),在gcc4.8.5下是不行的。這個(gè)是容易理解的,vector是個(gè)模板,它不能辨別傳入的數(shù)據(jù)類(lèi)型是否是指針,從而也不能進(jìn)行自動(dòng)的釋放內(nèi)存操作。如果對(duì)非new出的對(duì)象進(jìn)行delete操作,反而還會(huì)引起一些不必要的問(wèn)題。
C++11標(biāo)準(zhǔn)庫(kù)為了能夠使程序員能夠更安全的使用動(dòng)態(tài)內(nèi)存,提供了兩種智能指針類(lèi)型來(lái)管理動(dòng)態(tài)對(duì)象。
shared_ptr類(lèi)
智能指針也是模板,所以當(dāng)我們創(chuàng)建一個(gè)智能指針時(shí)也需要提供額外的信息——指針可以指向的類(lèi)型。例如:
shared_ptr<string> sp1; //sp1是這個(gè)智能指針的名字,尖括號(hào)里的string表示這個(gè)智能指針指向的是一個(gè)string類(lèi)型的變量(記住,雖然這里沒(méi)有我們熟悉的? * ,但sp1是一個(gè)指針),默認(rèn)初始化的智能指針中保存者一個(gè)空指針。
智能指針的使用和普通指針一致,解引用一個(gè)指針?lè)祷厮赶虻膶?duì)象,如果在一個(gè)條件判斷中使用智能指針效果就是檢測(cè)它是否為空。
shared_ptr<string> sp1; if(sp1) {std::cout << "判斷智能指針sp1是否為空指針" << std::endl; } if(sp1!= nullptr ) {if(sp1->empty()){*sp1 = "Hi Smart Pointer!"; //給該智能指針指向的字符串賦值std::cout << "判斷該智能指針指向的字符串是否為空!" << std::endl;} }
shared_ptr和unique_ptr都支持的操作:
| shared_ptr<T> sp / unique_ptr<T> up | 聲明一個(gè)智能指針,默認(rèn)初始化時(shí),該智能指針中保存著一個(gè)空指針 |
| p | 將p作為一個(gè)條件判斷,若p指向一個(gè)對(duì)象,則為true(和普通指針一樣,就是判斷指針本身是否為nullptr) |
| *p | 解引用p,獲得它指向的對(duì)象。和普通指針的*p語(yǔ)義一樣。 |
| p->mem | 等價(jià)于(*p).mem |
| p.get() | 返回p中保存的指針,但是這個(gè)方法使用起來(lái)要小心,如果智能指針釋放了其對(duì)象,返回的指針?biāo)赶虻膶?duì)象也就消失了 |
| swap(p.q) | 交換p和q中的指針 |
| p.swap(q) | 和swap(p.q)的作用是一樣的 |
shared_ptr獨(dú)有的操作
| make_shared<T>(args) | args:這種表達(dá)在C++primer里就是參數(shù)列表的意思。make_shared<T>(args)返回一個(gè)shared_ptr指針,指向一個(gè)動(dòng)態(tài)分配的類(lèi)型為T(mén)的對(duì)象,使用args來(lái)初始化此對(duì)象(相當(dāng)于是給構(gòu)造函數(shù)傳參) |
| shared_ptr<T>p(q) | p是shared_ptr q的拷貝,此操作會(huì)遞增q中的引用計(jì)數(shù)器。q中的指針必須要能轉(zhuǎn)換成T* |
| p = q | p和q都是shared_ptr,所保存的指針必須能夠相互轉(zhuǎn)換,此操作會(huì)遞減p的引用計(jì)數(shù),遞增q的引用計(jì)數(shù);若p的一用計(jì)數(shù)變?yōu)?,則將其管理的原內(nèi)存釋放 |
| p.unique() | 若p.use_count()為1則返回true;否則返回false |
| p.use_count() | 返回與p共享對(duì)象的智能指針的數(shù)量;可能很慢,主要用于調(diào)試。 |
shared_ptr和unique_ptr的區(qū)別:shared_ptr允許多個(gè)指針指向同一個(gè)對(duì)象,unique_ptr則“獨(dú)占”所指向的對(duì)象。weak_ptr是一種伴隨類(lèi),是一種弱引用,指向shared_ptr所管理的對(duì)象。這三種類(lèi)型都定義在頭文件memory中。
示例:
#include <iostream> #include <string> #include <memory>using namespace std; int main(int argc,char *argv[]) {shared_ptr<int> sp1;if(nullptr == sp1){std::cout << "shared_ptr默認(rèn)初始化時(shí),其內(nèi)保存著一個(gè)空指針" << std::endl;}if(sp1.use_count() == 0){std::cout << "默認(rèn)初始化時(shí),這個(gè)智能指針的引用計(jì)數(shù)值是:" << sp1.use_count() << std::endl;}shared_ptr<int> sp2 = make_shared<int>(42); //聲明一個(gè)指向int類(lèi)型的智能指針,并將其初始化為42,sp2這個(gè)智能指針的引用計(jì)數(shù)值應(yīng)該是1std::cout << "這個(gè)智能指針內(nèi)保存的值是:" << *sp2 << ",它的引用計(jì)數(shù)器的值是:" << sp2.use_count() << std::endl;sp1 = sp2; //無(wú)論何時(shí),拷貝一個(gè)shared_ptr,它的引用計(jì)數(shù)器都會(huì)遞增,例如將用一個(gè)shared_ptr初始化另外一個(gè)shared_ptr,或者將它作為參數(shù)傳遞給一個(gè)函數(shù),以及作為函數(shù)的返回值//當(dāng)我們給shared_ptr賦予了一個(gè)新值或者是shared_ptr被銷(xiāo)毀,它所關(guān)聯(lián)的引用計(jì)數(shù)器就會(huì)遞減,一旦一個(gè)shared_ptr的計(jì)數(shù)器變?yōu)?,它就會(huì)自動(dòng)釋放自己所管理的對(duì)象.std::cout << "sp1的引用計(jì)數(shù)值是:" << sp1.use_count() << ",sp2的引用計(jì)數(shù)值是:" << sp2.use_count() << std::endl;shared_ptr<int> sp3 = make_shared<int>(32);sp2 = sp3; //給sp2賦予一個(gè)新的值,這里sp2里關(guān)聯(lián)到原來(lái)指向的那個(gè)對(duì)象的引用計(jì)數(shù)器值會(huì)遞減,但同時(shí),它又被指向了新的對(duì)象,這個(gè)關(guān)聯(lián)到新對(duì)象的引用計(jì)數(shù)值又會(huì)增加。std::cout << "sp2關(guān)聯(lián)的計(jì)數(shù)器值是:" << sp2.use_count() << ",sp3關(guān)聯(lián)的引用計(jì)數(shù)器值是:" << sp3.use_count() << "sp1關(guān)聯(lián)的應(yīng)用計(jì)數(shù)器值是:" << sp1.use_count() << std::endl;//當(dāng)指向一個(gè)對(duì)象的最后一個(gè)shared_ptr被銷(xiāo)毀時(shí),shared_ptr類(lèi)會(huì)自動(dòng)銷(xiāo)毀此對(duì)象,通過(guò)析構(gòu)函數(shù)完成此工作。shared_ptr的析構(gòu)函數(shù)會(huì)遞減它所指向的對(duì)象的引用計(jì)數(shù)。如果引用計(jì)數(shù)變?yōu)?,則shared_ptr//的析構(gòu)函數(shù)就會(huì)銷(xiāo)毀對(duì)象,并釋放它占用的內(nèi)存, {shared_ptr<int> sp4 = make_shared<int>(3);sp1 = sp4;}if(sp1!= nullptr){std::cout << "上面的sp4雖然被銷(xiāo)毀了,但是由于有sp1=sp4這個(gè)賦值操作,導(dǎo)致指向sp4原本指向的對(duì)象的指針對(duì)于0個(gè),那么它申請(qǐng)的內(nèi)存就不會(huì)隨著sp4的析構(gòu)而銷(xiāo)毀。" << "sp1當(dāng)前所保存的值是: " << *sp1 << std::endl;}//shared_ptr在無(wú)用之后仍然保留的一種情況是,你將shared_ptr存放在一個(gè)容器中,隨后重排了容器,從而不再需要某些元素,在這種情況下,你應(yīng)該確保erase刪除那些不需要的shared_ptr元素。return (0); }分配動(dòng)態(tài)內(nèi)存的幾個(gè)理由:
1.不知道自己想要多大的空間;
2.不知道對(duì)象的類(lèi)型是什么,(void *)
3.需要多個(gè)對(duì)象共享數(shù)據(jù)
程序非自由空間被耗盡的情況還是有可能發(fā)生的,一旦一個(gè)程序用光了它所有可用的內(nèi)存,new表達(dá)式就會(huì)失敗。默認(rèn)情況下,如果new不能分配所要求的的內(nèi)存空間,它會(huì)拋出一個(gè)類(lèi)型為bad_alloc的異常??梢愿淖兪褂胣ew的方式來(lái)阻止它拋出異常。
int *p2 = new (nothrow) int; //此時(shí)如果分配內(nèi)存失敗,那么new就會(huì)返回一個(gè)空指針,這種形式的new稱(chēng)為定位new,定位new表達(dá)式允許我們向new傳遞額外參數(shù)。這個(gè)例子中傳入的是一個(gè)由標(biāo)準(zhǔn)庫(kù)定義的名為nothrow的對(duì)象,意思就是不拋出異常。這個(gè)都定義在頭文件new中new運(yùn)算符包含兩個(gè)動(dòng)作:分配內(nèi)存,構(gòu)造對(duì)象。
delete運(yùn)算符負(fù)責(zé)釋放new運(yùn)算符分配的內(nèi)存,把它還給操作系統(tǒng),delete也包含兩個(gè)動(dòng)作,銷(xiāo)毀給定的指針指向的對(duì)象,釋放對(duì)應(yīng)的內(nèi)存。
傳給delete的指針必須是動(dòng)態(tài)分配的內(nèi)存或者是一個(gè)空指針,釋放一塊并非new分配的內(nèi)存,或者將相同的指針釋放多次,其行為是未定義的。
動(dòng)態(tài)對(duì)象的生存期直到被釋放時(shí)為止。(new/delete? new出來(lái)的指針被稱(chēng)為內(nèi)置指針)
動(dòng)態(tài)內(nèi)存的管理里容易出現(xiàn)的幾個(gè)問(wèn)題:
1.忘記delete內(nèi)存。忘記釋放動(dòng)態(tài)內(nèi)存常會(huì)導(dǎo)致“內(nèi)存泄漏”問(wèn)題,因?yàn)檫@種內(nèi)存永遠(yuǎn)不可能歸還給自由空間了。
2.使用已經(jīng)釋放掉的對(duì)象。通過(guò)在釋放內(nèi)存后將指針置為空,有時(shí)可以檢測(cè)出這種錯(cuò)誤。如果不置為空,那么會(huì)產(chǎn)生空懸指針(野指針)。這會(huì)造成災(zāi)難性的后果。
3.同一塊內(nèi)存釋放兩次,當(dāng)有兩個(gè)指針指向相同的動(dòng)態(tài)分配對(duì)象時(shí),可能發(fā)生這種錯(cuò)誤。如果對(duì)其中一個(gè)指針進(jìn)行了delete操作,對(duì)象的內(nèi)存就被歸還給自由空間了,如果隨后又對(duì)第二個(gè)指針進(jìn)行delete操作,自由空間就可能被破壞。
shared_ptr和new的結(jié)合使用
主要用于shared_ptr的初始化。使用示例如下:
shared_ptr<int> sp1 = new int(0); //這是錯(cuò)誤的 shared_ptr<int> sp2(new int(0)); //這才是正確的寫(xiě)法默認(rèn)情況下,一個(gè)用來(lái)初始化智能指針的普通指針必須要指向動(dòng)態(tài)內(nèi)存,因?yàn)橹悄苤羔樐J(rèn)使用delete來(lái)釋放它所關(guān)聯(lián)的對(duì)象。
| shared_ptr<T> p(q) | p管理內(nèi)置指針q所指向的對(duì)象,q必須指向new分配的內(nèi)存,且能夠轉(zhuǎn)換為T(mén)* |
| shared_ptr<T> p(u) | p從unqiue_ptr u那里接管了對(duì)象的所有權(quán)。將u置為空 |
| shared_ptr<T> p(q,d) | p接管了內(nèi)置指針q所指向的對(duì)象得到所有權(quán)。q必須能夠轉(zhuǎn)換為T(mén)*類(lèi)型,p將使用可調(diào)用對(duì)象d來(lái)代替delete |
| shared_ptr<T> p(p2,d) | p是shared_ptr p2的額拷貝,唯一的區(qū)別是,p將可調(diào)用對(duì)象d來(lái)代替delete |
| p.reset() | 若p是唯一指向其對(duì)象的shared_ptr,reset會(huì)釋放此對(duì)象。 |
| p.reset(q) | 若傳遞了可選的參數(shù)內(nèi)置指針q,會(huì)令p指向q,否則會(huì)將p置為空 |
| p.reset(q,d) | 傳遞了內(nèi)置指針q和可調(diào)用對(duì)象d,那么會(huì)使用d來(lái)替代delete |
不要使用get初始化另外一個(gè)智能指針或?yàn)橹悄苤羔樫x值,get是用來(lái)給那些只能使用內(nèi)置指針的地方來(lái)用的。
在使用reset之前,我們要檢查自己是不是當(dāng)前對(duì)象僅有的用戶(hù),如果不是,那么在使用之前要制作一份新的拷貝。函數(shù)退出的兩種情況:正常退出和發(fā)生異常。無(wú)論哪種情況,局部對(duì)象都會(huì)被銷(xiāo)毀。
智能指針使用規(guī)范:
- 不使用相同的內(nèi)置指針值初始化或reset多個(gè)智能指針
- 不delete get返回的指針
- 不使用get()初始化或reset另一個(gè)智能指針
- 如果你使用了get()返回的指針,記住當(dāng)最后一個(gè)對(duì)應(yīng)的智能指針?shù)N毀后,你的指針就無(wú)效了
- 如果你使用智能指針管理的資源不是new分配內(nèi)存,記住傳遞給它一個(gè)刪除器。
轉(zhuǎn)載于:https://www.cnblogs.com/ToBeExpert/p/10045948.html
總結(jié)
以上是生活随笔為你收集整理的标准库中的智能指针shared_ptr的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Lodop导出图片和打印机无关,测试是否
- 下一篇: [源码和文档分享]基于FFMEPEG-M