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