C++總結8——shared_ptr和weak_ptr智能指針

http://blog.csdn.net/wendy_keeping/article/details/75268687

智能指針的提出:智能指針是存儲指向動態分配對象指針的類,用于生存期控制。能夠確保正確銷毀動態分配的內存,防止內存泄露。

1.智能指針的分類:?
不帶引用計數的智能指針?
auto_ptr unique_ptr scoped_ptr

帶引用計數的智能指針?
shared_ptr:強智能指針?
weak_ptr:若智能指針

2.不帶引用計數的智能指針是如何確保正確釋放堆上內存的??
auto_ptr:

auto_ptr<A> ptr1(new A(10));
auto_ptr<A> ptr2 = ptr1;
  • 1
  • 2

當這兩行代碼執行完后,ptr1已經不再指向A這個對象,并且ptr1=NULL,ptr1對A對象的所有權已經轉移給了ptr2,也就不能通過ptr1調用該類的方法。

vector<auto_ptr<int>> vec;

不能將auto_ptr類型的指針作為STL容器的元素。因為容器避免不了直接拷貝構造和互相賦值。auto_ptr的這種特性會給STL容器的使用造成很大的麻煩。

unique_ptr:

unique_ptr<A> ptr1(new A(10));
unique_ptr<A> ptr2 = ptr1;//error
unique_ptr<A> ptr3 = func();//ok

unique_ptr不支持拷貝構造函數和賦值運算符重載函數,不能隱式的將原指針置為NULL,但是可以通過返回值賦值。unique_ptr支持移動構造函數和移動賦值運算符重載函數,是通過move(C++11標準)函數實現的,用戶明確知道對象的所有權已經發生轉移,不能再使用原來的指針調用類的方法。這也是auto_ptr和unique_ptr最根本的區別。

scoped_ptr:

scoped_ptr不支持所有權的轉移,只能通過該指針對對象進行操作,出作用域會自動析構。

3.帶引用計數的智能指針

shared_ptr<A> pa(new A());
shared_ptr<A> pb = pa;

當使用指針指向堆上的對象時,該對象的引用計數加1。拷貝構造或賦值時,不產生該對象的副本,只是更改對象的引用計數。如果別的智能指針也指向這個對象時,也是增加其引用計數。如果delete 指針時,先對引用計數減1,直到對象的引用計數減為0,才調用該對象的析構函數。

4.智能指針的交叉引用導致內存泄露

class A
{
public:shared_ptr<B> _pb;
};
class B
{
public:shared_ptr<A> _pa;
};int main()
{shared_ptr<A> pa(new A());shared_ptr<B> pb(new B());pa->_pb = pb;pb->_pa = pa;return 0;
}

pa指針指向堆上對象后,A的引用計數是1,B的引用計數是1。?
pa->_pb = pb;此時,B的引用計數是2。?
pb->_pa = pa;此時,A的引用計數是2。

出作用域后:?
pb指針釋放,B對象的引用計數減1,此時,B的引用計數是1,并不調用B的析構函數。?
pa指針釋放,A對象的引用計數減1,此時,A的引用計數是1,并不調用A的析構函數。

程序運行結束,A和B對象并未析構,造成內存泄露

為了避免內存泄露,必須打斷這種循環等待的現象。智能指針提供了weak_ptr(弱智能指針)可以避免產生循環等待的現象。

weak_ptr,當用weak_ptr指向對象時,對象的引用計數并不加1,因為弱智能指針沒有對對象的使用權,它只有監控權。

#include <iostream>
#include <memory>
using namespace std;class A
{
public:weak_ptr<B> _pb;
};
class B
{
public:shared_ptr<A> _pa;
};int main()
{shared_ptr<A> pa(new A());shared_ptr<B> pb(new B());pa->_pb = pb;pb->_pa = pa;return 0;
}

pa指針指向堆上對象后,A的引用計數是1,B的引用計數是1。?
pa->_pb = pb;此時,B的引用計數是1。因為A的成員對象是weak_ptr。?
pb->_pa = pa;此時,A的引用計數是2。

出作用域后:?
pb指針釋放,B對象的引用計數減1,此時,B的引用計數是0,調用B的析構函數,先析構自己(B被析構),再析構成員對象(A的引用計數減1,此時A的引用計數是1)?
pa指針釋放,A對象的引用計數減1,此時,A的引用計數是0,調用A的析構函數(A被析構)

為了避免智能指針循環引用造成的內存泄露情況,要遵守:創建對象的時候,一定要用強智能指針,其他地方只能用持有資源的弱智能指針

智能指針本身是線程安全的。使用atomic_increment和atimic_decreament進行對多線程間的互斥,保證引用計數count++/count–是原子操作。

5.多線程訪問共享的C++對象產生的線程安全的問題

#include <iostream>
#include <memory>
using namespace std;class C
{
public:C(){}~C(){}void func(){cout<<"call func()"<<endl;}
};void* threadProc(void *lparg)
{shared_ptr<C> pc = (shared_ptr<C>)lparg;pc->func();//如果對象已經析構,則程序崩潰!return NULL;
}int main(int argc, char* argv[])
{shared_ptr<C> c(new C());pthread_t tid;pthread_create(&tid, NULL, threadProc, &c);return 0;
}

多線程中可能訪問共享的對象,如果對象已經析構,線程中訪問它,程序會崩潰掉。解決多線程訪問共享的C++對象產生的線程安全的問題,可以使用weak_ptr。將弱智能指針轉給線程,再訪問對象時,先將弱智能和提升為強智能指針,如果提升成功,則說明對象還存在,可以訪問該對象的成員變量或方法。如果提升失敗,則說明對象已經析構。


#include <iostream>
#include <memory>
using namespace std;class C
{
public:C(){}~C(){}void func(){cout<<"call func()"<<endl;}
};void* threadProc(void *lparg)
{weak_ptr<C> pw = *(weak_ptr<C>*)lparg;shared_ptr<C> pc = pw.lock();
//類型提升if(pc != NULL)
//類型提升成功,對象未析構{pc->func();}return NULL;
}int main(int argc, char* argv[])
{shared_ptr<C> c(new C());pthread_t tid;weak_ptr<C> pw(c);pthread_create(&tid, NULL, threadProc, &pw);return 0;
}

創建對象的時候一定要用強智能指針,其他地方只能用持有資源的弱智能指針!!!


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383785.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383785.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383785.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++析構函數執行順序

今天發現主程序中有多個對象時析構函數的執行順序不是對象定義的順序&#xff0c;而是對象定義順序反過來。 思考了一下&#xff0c;結合之前繼承、成員對象等的析構函數執行的順序&#xff0c;我覺得析構函數執行的順序為&#xff1a;構造函數的順序反過來&#xff0c;可能是…

c++寫時拷貝1

http://blog.csdn.net/SuLiJuan66/article/details/48882303 Copy On Write Copy On Write(寫時復制)使用了“引用計數”&#xff08;reference counting&#xff09;&#xff0c;會有一個變量用于保存引用的數量。當第一個類構造時&#xff0c;string的構造函數會根據傳入的參…

【C++學習筆記五】模板

模板是泛型編程的基礎 函數模板 模板定義以關鍵字template開始&#xff0c;后跟一個模板參數列表。這是一個逗號分隔的一個或多個模板參數的列表。用尖括號包圍起來。 模板函數定義的一般形式&#xff1a; template <class type> ret-tye func-name(parameter list) …

【Java學習筆記十】輸入輸出流

在Java.io包中提供了一系列用于處理輸入/輸出的流類。從功能上分為兩類&#xff1a;輸入流和輸出流。從六結構上可分為&#xff1a;字節流&#xff08;以字節為處理單位&#xff09;和字符流&#xff08;以字符為處理單位&#xff09;。 字符是由字節組成。在Java中所有字符用…

C++ 寫時拷貝 2

什么情況下會用到c中的拷貝構造函數】&#xff1a; 1&#xff09;用已經存在的同類的對象去構造出另一個新的對象 2&#xff09;當函數的形參是類的對象時&#xff0c;這時調用此函數&#xff0c;使用的是值的拷貝&#xff0c;也會調用拷貝構造函數 3&#xff09;當函數的返…

【Java學習筆記十一】圖形用戶界面

圖形用戶界面或圖形用戶接口(Graphical User Interface&#xff0c;GUI)是指采用圖形方式,借助菜單、按鈕等標準界面元素&#xff0c;用戶可以通過鼠標等外設向計算機系統發出指令、啟動操作&#xff0c;并將系統運行的結果同樣以圖形方式顯示給用戶的技術。 GUI是事件驅動的&…

C++ 寫時拷貝 3

http://blog.csdn.net/ljianhui/article/details/22895505 字符串一種在程序中經常要使用到的數據結構&#xff0c;然而在C中卻沒有字符串這種類型。在C中&#xff0c;為了方便字符串的使用&#xff0c;在STL中提供了一個string類。該類維護一個char指針&#xff0c;并封裝和提…

C++類模板實例化條件

&#xff08;我不想了解這個&#xff0c;可是考試要考。。。。 并不是每次使用模板類都會實例化一個類 聲明一個類模板的指針和引用不會引起類模板的實例化如果檢查這個指針或引用的成員時時&#xff0c;類模板會實例化定義一個對象的時候需要有類的定義&#xff0c;會實例化…

C++ String類寫時拷貝 4

http://blog.51cto.com/zgw285763054/1839752 維基百科&#xff1a; 寫入時復制&#xff08;英語&#xff1a;Copy-on-write&#xff0c;簡稱COW&#xff09;是一種計算機程序設計領域的優化策略。其核心思想是&#xff0c;如果有多個調用者&#xff08;callers&#xff09;同時…

C++筆試復習

基礎知識點 C中對象數組在定義的時候全部進行實例化&#xff08;與Java不同&#xff0c;Java相當于只是定義了一個指針數組&#xff0c;沒有進行實例化&#xff09; 程序的三種基本控制結構是&#xff1a;順序結構、循環結構、選擇結構 一個C程序開發步驟通常包括&#xff1a…

C++函數默認參數

聲明是用戶可以看到的部分&#xff0c;客戶非常信任地使用這個特性&#xff0c;希望得到一定的結果&#xff0c;但是你在實現里使用了不同的缺省值&#xff0c;那么將是災難性的。因此編譯器禁止聲明和定義時同時定義缺省參數值。 類的成員函數的參數表在聲明時默認參數位于參…

C語言鏈表各類操作詳解

http://blog.csdn.net/pf4919501/article/details/38818335鏈表概述   鏈表是一種常見的重要的數據結構。它是動態地進行存儲分配的一種結構。它可以根據需要開辟內存單元。鏈表有一個“頭指針”變量&#xff0c;以head表示&#xff0c;它存放一個地址。該地址指向一個元素。…

Java筆試復習

Java程序運行 Java程序的執行必須經過編輯、編譯和運行三個步驟 編輯指編寫代碼&#xff0c;最終形成后綴名為.java的Java源文件編譯指使用Java編譯器&#xff08;javac指令&#xff09;將源文件翻譯為二進制代碼&#xff0c;編譯后生成后綴名為.class的字節碼文件&#xff0c…

數據結構之自建算法庫——鏈棧

http://blog.csdn.net/sxhelijian/article/details/48463801本文針對數據結構基礎系列網絡課程(3)&#xff1a;棧和隊列中第4課時棧的鏈式存儲結構及其基本運算實現。 按照“0207將算法變程序”[視頻]部分建議的方法&#xff0c;建設自己的專業基礎設施算法庫。 鏈棧算法庫采用…

Java類名與包名不區分大小寫

剛才寫了一個簡單的Java程序&#xff0c;經過測試得到一個令人震驚的結論&#xff1a;Java類名和包名是不區分大小寫的 可以看一下這個例子&#xff1a; package Test;class aBcdEfG {}class AbCdefg {}public class TTT {public static void main(String[] args){AbCdefg tm…

epoll實現高并發聊天室

http://blog.csdn.net/qq_31564375/article/details/51581038項目介紹 本項目是實現一個簡單的聊天室&#xff0c;聊天室分為服務端和客戶端。本項目將很多復雜的功能都去掉了&#xff0c;線程池、多線程編程、超時重傳、確認收包等等都不會涉及。總共300多行代碼&#xff0c;讓…

BZOJ2809-左偏樹合并

Description 在一個忍者的幫派里&#xff0c;一些忍者們被選中派遣給顧客&#xff0c;然后依據自己的工作獲取報償。在這個幫派里&#xff0c;有一名忍者被稱之為 Master。除了 Master以外&#xff0c;每名忍者都有且僅有一個上級。為保密&#xff0c;同時增強忍者們的領導力&a…

處理大并發之一 對epoll的理解,epoll客戶端服務端代碼

http://blog.csdn.net/zhuxiaoping54532/article/details/56494313處理大并發之一對epoll的理解&#xff0c;epoll客戶端服務端代碼序言&#xff1a;該博客是一系列的博客&#xff0c;首先從最基礎的epoll說起&#xff0c;然后研究libevent源碼及使用方法&#xff0c;最后研究n…

epoll詳解

http://blog.csdn.net/majianfei1023/article/details/45772269歡迎轉載&#xff0c;轉載請注明原文地址&#xff1a;http://blog.csdn.net/majianfei1023/article/details/45772269一.基本概念&#xff1a;1.epoll是什么&#xff1a;epoll是Linux內核為處理大批量文件描述符而…

數據分割-并查集+set

小w來到百度之星的賽場上&#xff0c;準備開始實現一個程序自動分析系統。 這個程序接受一些形如xixj 或 xi≠xj 的相等/不等約束條件作為輸入&#xff0c;判定是否可以通過給每個 w 賦適當的值&#xff0c;來滿足這些條件。 輸入包含多組數據。 然而粗心的小w不幸地把每組數據…