日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

智能指针shared_ptr

發布時間:2024/4/18 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 智能指针shared_ptr 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

如果有可能就使用unique_ptr,然后很多時候對象是需要共享的,因此shared_ptr也就會用得很多。shared_ptr允許多個指向同一個對象,當指向對象的最后一個shared_ptr銷毀時,該對象也就會自動銷毀。因此,善用shared_ptr,能夠遠離內存泄漏。

基本使用

它的很多操作與unique_ptr類似。下面是三種常見的定義方式:

shared_ptr<int> sp;//聲明一個指向int類型的智能指針 sp.reset(new int(42)); if(sp){cout << "sp is not null" <<endl; } auto sp1 = make_shared<string>("hello");//sp1是一個智能指針 shared_ptr sp2(new int(42));

而make_shared方式是推薦的一種,它使用一次分配,比較安全。

不能將一個原始指針直接賦值給一個智能指針,例如,下面這種方法是錯誤的:

std::shared_ptr<int> p = new int(1); //編譯報錯,不允許直接賦值

shared_ptr不能通過直接將原始指針賦值來初始化,需要通過構造函數和輔助方法來初始化。對于一個未初始化的智能指針,可以通過reset方法來初始化,當智能指針中有值的時候,調用reset會使引用計數減1。另外,智能指針可以通過重載的bool類型操作符來判斷智能指針中是否為空(未初始化)。

獲取原始指針

當需要獲取原始指針時,可以通過get方法來返回原始指針,代碼如下:

std::shared_ptr<int> ptr(new int(1)); int* p = ptr.get();

哪些操作會改變計數

我們都知道,當引用計數為0時,shared_ptr所管理的對象自動銷毀,那么哪些情況會影響引用計數呢?

賦值

例如:

auto sp = make_shared<int>(1024);//sp的引用計數為1

再比如:

auto sp1 = make_shared<string>("obj1"); auto sp2 = make_shared<string>("obj2"); auto sp1 = sp2;

該操作會減少sp1的引用計數,增加sp2的引用計數。有的人可能不理解,為什么這樣還會減少sp1的引用計數?試想一下,sp1指向對象obj1,sp2指向對象obj2,那么賦值之后,sp1也會指向obj2,那就是說指向obj1的就少了,指向obj2的就會多,如果此時沒有其他shared_ptr指向obj1,那么obj1將會銷毀。

拷貝

例如:

auto sp2 = make_shared<int>(1024); auto sp1(sp2);

該操作會使得sp1和sp2都指向同一個對象。而關于拷貝比較容易忽略的就是作為參數傳入函數:

auto sp2 = make_shared<int>(1024); func(sp2);//func的執行會增加其引用計數

可以看一個具體的例子:

#include<iostream> #include<memory> void func0(std::shared_ptr<int> sp) {std::cout<<"fun0:"<<sp.use_count()<<std::endl; } void func1(std::shared_ptr<int> &sp) {std::cout<<"fun1:"<<sp.use_count()<<std::endl; } int main() {auto sp = std::make_shared<int>(1024);func0(sp);func1(sp);return 0; }

其運行輸出結果為:

fun0:2 fun1:1

很顯然,fun0,拷貝了shard_ptr sp,而fun1,并沒有拷貝,因此前者會增加引用計數,計數變為2,而后者并不影響。

reset

調用reset會減少計數:

sp.reset()

而如果sp是唯一指向該對象的,則該對象被銷毀。

應當注意使用的方式

雖然shared_ptr能很大程度避免內存泄漏,但是使用不當,仍然可能導致意外發生。

存放于容器中的shared_ptr

如果你的容器中存放的是shared_ptr,而你后面又不再需要它時,記得使用erase刪除那些不要的元素,否則由于引用計數一直存在,其對象將始終得不到銷毀,除非容器本身被銷毀。

不要使用多個裸指針初始化多個shared_ptr

注意,下面方式是不該使用的:

#include<iostream> #include<memory> int main() {auto *p = new std::string("hello");std::shared_ptr<std::string> sp1(p);/*不要這樣做!!*/std::shared_ptr<std::string> sp2(p);return 0; }

這樣會導致兩個shared_ptr管理同一個對象,當其中一個被銷毀時,其管理的對象會被銷毀,而另外一個銷毀時,對象會二次銷毀,然而實際上,對象已經不在了,最終造成嚴重后果。而與這種情況類似的,就是使用get()獲取裸指針,然后去初始化另外一個shared_ptr,或者delete get返回的指針:

#include<iostream> #include<memory> int main() {auto sp = std::make_shared<std::string>("wechat:shouwangxiansheng");std::string *p = sp.get();std::shared_ptr<std::string> sp2(p);/*不要這樣做!!*/delete p;/*不要這樣做*/return 0; }

循環引用

在對象之間出現循環引用時,會使得共享指針引用計數不會降到0,也就不能銷毀。

?

#include <iostream> #include <memory> using std::shared_ptr; using std::make_shared; // 一段內存泄露的代碼 struct Son; struct Father{shared_ptr<Son> son_; }; struct Son{shared_ptr<Father> father_; }; int main() {auto father = make_shared<Father>();auto son = make_shared<Son>();father->son_ = son;son->father_ = father;std::cout<<"one father's son:"<<father.use_count()<<std::endl; std::cout<<"one son's father:"<<son.use_count()<<std::endl; return 0; }

編譯運行結果為:

xhy@ubuntu:~/cpp_learn/share_ptr$ ./test one father's son:2 one son's father:2

函數結束前,堆上的兩個對象的引用計數都是2,所以即便函數結束,將兩個棧上的的共享指針分別析構,最后堆上的兩個對象的引用數也不會為0,而是1,兩個對象不會調用析構函數進行析構,從而內存泄漏。參考weak_ptr可以解決。

如果對象不是new分配的,請傳遞刪除器

與unique_ptr類似,它可以指定刪除器,默認是使用delete。例如:

#include<iostream> #include<unistd.h> #include<memory> void myClose(int *fd) {close(*fd); } int main() {int socketFd = 10;//just for examplestd::shared_ptr<int> up(&socketFd,myClose);return 0; }

關于指定刪除器

智能指針初始化可以指定刪除器,代碼如下:

void DeleteIntPtr(int* p) {delete p; } std::shared_ptr<int> p(new int,DeleteIntPtr);

當p的引用計數為0時,自動調用刪除器DeleteIntPtr來釋放對象的內存。刪除器可以是一個lambda表達式,因此,上面的寫法還可以改為:

std::shared_ptr<int> p(new int,[](int* p){delete p;});

當我們用shared_ptr管理動態數組時,需要指定刪除器,因為std::shared_ptr的默認刪除器不支持數組對象,代碼如下:

std::shared_ptr<int> p(new int[10],[](int* p){delete[] p;}); //指定delete[]

也可以將std::default_delete作為刪除器。default_delete的內部是通過調用delete來實現功能的,代碼如下:

std::shared_ptr<int> p(new int[10],std::default_delete <int[]>);

另外,還可以通過封裝一個make_shared_array方法來讓shared_ptr支持數組,代碼如下:

template<typename> shared_ptr<T> make_shared_array(size_t size) {return shared_ptr<T>(new T[size],default_delete<T[]>()); }

測試代碼如下:

std::shared_ptr<int> p = make_shared_array<int>(10); std::shared_ptr<char> p = make_shared_array<char>(10);

與unique_ptr的區別

首先最明顯的區別自然是它們一個是專享對象,一個是共享對象。而正是由于共享,包括要維護引用計數等,它帶來的開銷相比于unique_ptr來說要大。另外,shared_ptr無法直接處理數組,因為它使用delete來銷毀對象,而對于數組,需要用delete[]。因此,需要指定刪除器:

#include<iostream> #include<memory> int main() {auto sp = std::make_shared<std::string>("wechat:shouwangxiansheng");std::string *p = sp.get();//std::shared_ptr<int> sp1(new int[10]);//不能這樣std::shared_ptr<int> sp1(new int[10],[](int *p){delete[] p;});return 0; }

示例中使用了lambda表達式。不過一般來說,好好的容器不用,為什么要用動態數組呢?

參考資料:https://xhy3054.github.io/cpp-shared-ptr/

總結

以上就是shared_ptr基本內容,一般來說,規范使用shared_ptr能很大程度避免內存泄露。注意,shared_ptr提供,*,->操作,不直接提供指針運算和[]。

總結

以上是生活随笔為你收集整理的智能指针shared_ptr的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。