c++中智能指針的使用,個人總結

来源:https://www.cnblogs.com/wenxiang-wenxiang/archive/2022/06/25/16409990.html
-Advertisement-
Play Games

一、什麼是智能指針 一般來講C++中對於指針指向的對象需要使用new主動分配堆空間,在使用結束後還需要主動調用delete釋放這個堆空間。為了使得自動、異常安全的對象生存期管理可行,就出現了智能指針這個概念。簡單來看智能指針是 RAII(Resource Acquisition Is Initial ...


一、什麼是智能指針

  一般來講C++中對於指針指向的對象需要使用new主動分配堆空間,在使用結束後還需要主動調用delete釋放這個堆空間。為了使得自動、異常安全的對象生存期管理可行,就出現了智能指針這個概念。簡單來看智能指針是 RAII(Resource Acquisition Is Initialization,資源獲取即初始化) 機制對普通指針進行的一層封裝。這樣使得智能指針的行為動作像一個指針,本質上卻是一個對象,這樣可以方便管理一個對象的生命周期。

  智能指針作用總結:

  1. 處理記憶體泄漏。
  2. 處理空懸指針的問題。
  3. 處理異常造成的記憶體泄露。

  註:智能指針和原生指針不要混用,使用不當可能會導致程式異常;

二、智能指針有哪些

  智能指針(動態記憶體管理)頭文件 <memroy>

三、獨占式智能指針(std::unique_ptr)

 1 class SmartPointer
 2 {
 3 public:
 4     SmartPointer()
 5     {
 6         cout << "SmartPointer::SmartPointer()" << endl;
 7     }
 8     ~SmartPointer()
 9     {
10 
11         cout << "SmartPointer::~SmartPointer()" << endl;
12     }
13 };

 

  •   獨占式:
  •   unique_ptr擁有所指向(管理)的資源、對象的所有權,即不能被其他unique_ptr所指;
  •   unique_ptr不能進行賦值或拷貝操作;
    unique_ptr<SmartPointer> ptest1(new SmartPointer("空格"));
//    unique_ptr<SmartPointer> ptest2 = ptest1;     //此時編譯報錯
  •   但可以使用std::move或者relase()方法將源unique_ptr 指針指向資源所有權轉向新unique_ptr;
    unique_ptr<SmartPointer> ptest1(new SmartPointer("空格"));
    cout << "ptest1 location: " << ptest1.get() << endl;
    unique_ptr<SmartPointer> ptest2 = move(ptest1);         //將ptest1指向對象所有權轉移給ptest2,ptest1置空為NULL
    cout << "ptest2 location: " << ptest2.get() << endl;
    unique_ptr<SmartPointer> ptest3(ptest2.release());      //將ptest2指向對象所有權轉移給ptest3,ptest2置空為NULL
    cout << "ptest3 location: " << ptest2.get() << endl;

結果為:

  

 

  •   unique_ptr本身擁有的幾個主要方法
  1. get() 方法:獲取其保存的原生指針,儘量不要使用;
  2. release() 方法:釋放所管理指針的所有權,返回原生指針。但並不銷毀原生指針;
  3. reset() 方法:釋放並銷毀原生指針。如果參數為一個新指針,將管理這個新指針;
  4. bool() 方法:判斷是否擁有指針;
    unique_ptr<SmartPointer> ptest1(new SmartPointer());
    ptest1.reset(new SmartPointer());   //釋放銷毀原有對象,持有新對象
    ptest1.reset();     //直接釋放銷毀原對象
    ptest1 = nullptr;   //同上

四、共用式智能指針(std::shared_ptr)

  •   共用式

  共用權,多個shared_ptr同時擁有一個原生指針(記憶體)的所有權,最後一個擁有者負責原生指針的釋放和銷毀;

    std::shared_ptr<SmartPointer> pTest1(new SmartPointer());
    std::shared_ptr<SmartPointer> pTest2 = pTest1;              //編譯正常,允許所有權的共用
    cout  << "pTest1 location: " << pTest1.get() << endl;
    cout  << "pTest2 location: " << pTest2.get() << endl;
    shared_ptr<SmartPointer> pTest3 = make_shared<SmartPointer>();//新建共用指針方法,make_shared效果類似new
    pTest2 = pTest3;
    cout  << "pTest3 location: " << pTest3.get() << endl;
    cout  << "pTest2 location: " << pTest2.get() << endl;

結果:

  

  •   計數器

  共用指針類中包括一個成員函數用來記住所管理的記憶體當前有多少個指針指向它;

  use_count()方法可以獲取指向對象的shared_ptr個數;

    shared_ptr<string> pStr_1(new string("霜之哀傷"));
    shared_ptr<string> pStr_2 = make_shared<string>("火之高興");

    auto pStr_3 = pStr_1;   //此時指向“霜之哀傷”數量為2,“火之高興”為1;
    cout << *pStr_1 << " : " << pStr_1.use_count() << "\t"
         << *pStr_2 << " : " << pStr_2.use_count() << endl;
    pStr_3 = pStr_2;        //此時指向“霜之哀傷”數量為1,“火之高興”為2;
    cout << *pStr_1 << " : " << pStr_1.use_count() << "\t"
         << *pStr_2 << " : " << pStr_2.use_count() << endl;

結果:

  

 

 

  •  shared_ptr擁有的幾個主要方法:
  1. get() 方法:獲取其保存的原生指針,儘量不要使用;
  2. bool() 方法:判斷是否擁有指針;
  3. reset() 方法:釋放並銷毀原生指針。如果參數為一個新指針,將管理這個新指針;
  4. unique() 方法:如果引用計數為 1(即對象所有權唯一),則返回 true,否則返回 false;
  5. use_count() 方法:返回引用計數的大小;
    shared_ptr<SmartPointer> pTest1(new SmartPointer());
    cout << pTest1.unique() << endl;    //此時為true;
    shared_ptr<SmartPointer> pTest2 = pTest1;
    cout << pTest1.unique() << endl;    //此時為false
    pTest1.reset(new SmartPointer());   //釋放銷毀原有對象,持有新對象
    pTest1.reset();     //直接釋放銷毀原對象
    pTest1 = nullptr;   //同上

結果:

  

五、輔助指針/弱指針(std::weak_ptr)

class B;

class A
{
public:
    A()
    {
        cout << "A::A()" << endl;
    }
    ~A()
    {
        cout << "A::~A()" << endl;
    }
    shared_ptr<B> pB;
};

class B
{
public:
    B()
    {
        cout << "B::B()" << endl;
    }
    ~B()
    {
        cout << "B::~B()" << endl;
    }
    shared_ptr<A> pA;
};

  weak_ptr是為了輔助shared_ptr所引入的弱引用智能指針,主要是為瞭解決shared_ptr中迴圈引用(對象A持有對象B,對象B持有對象A,此時兩個對象的引用計數均為2;在跳出作用範圍時,兩個對象引用計數均減1但還有1,導致跳出作用範圍後兩個對象的資源沒有釋放銷毀,產生記憶體泄漏)的問題。

    shared_ptr<A> pA(new A());  //新建A類對象
    shared_ptr<B> pB(new B());  //新建B類對象

    pA->pB = pB;    //A類中持有B類 
    pB->pA = pA;    //B類中持有A類
    //此時構成了迴圈引用
    cout << pA.use_count() << endl;
    cout << pB.use_count() << endl;

結果:

  迴圈引用在跳出作用範圍時未調用類A與類B析構函數,導致記憶體泄漏,此時可以將類內的shared_ptr改為weak_ptr可以避免;

  

 

   weak_ptr不能直接使用原生指針構造,可以使用一個shared_ptr和另一個weak_ptr進行構造;

  •   weak_ptr擁有的幾個主要方法
  1. expired() 方法:判斷所指向的原生指針是否被釋放,如果被釋放了返回 true,否則返回 false;
  2. use_count() 方法:返回原生指針的引用計數;
  3. lock() 方法:返回 shared_ptr,如果原生指針沒有被釋放,則返回一個非空的 shared_ptr,否則返回一個空的 shared_ptr;
  4. reset() 方法:將本身置空;
    shared_ptr<A> pA(new A());
    weak_ptr<A> weak_pA = pA;   //弱指針,不增加引用計數
    cout << "A引用計數:" << weak_pA.use_count() << endl;

    shared_ptr<A> pA_1 = weak_pA.lock();    //此時又一個shared_ptr指向原生指針,計數加1
    cout << "A引用計數:" << weak_pA.use_count() << endl;
    
    cout << weak_pA.expired() << endl;
    //銷毀原生指針
    pA.reset();
    pA_1.reset();
    cout << weak_pA.expired() << endl;
    weak_pA.reset();    //置空weak_ptr

結果:

  

參考:

https://www.cnblogs.com/corineru/p/10895249.html

https://www.cnblogs.com/TenosDoIt/p/3456704.html

https://zhuanlan.zhihu.com/p/436290273

https://www.csdn.net/tags/MtTaEg0sMTkwMTctYmxvZwO0O0OO0O0O.html

搜索

複製


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • TypeScript 是一種由微軟開發的自由和開源的編程語言,是一種非常受歡迎的 JavaScript 語言擴展,它也是 JavaScript 的一個超集,而且本質上向這個語言添加了可選的靜態類型和基於類的面向對象編程。它在現有的 JavaScript 語法之上加入了一層類型層,而這一層即使被刪除, ...
  • props傳遞數據 步驟: 首先,在子組件中聲明props選項 其次,在子組件中使用v-bind指令動態綁定屬性,通過插值表達式動態獲取數據 最後,在父組件的template中調用子組件標簽的使用傳遞數據 示例: 在子組件MovieItem.vue中 <template> <div class="s ...
  • 一、什麼是首屏載入 首屏時間(First Contentful Paint),指的是瀏覽器從響應用戶輸入網址地址,到首屏內容渲染完成的時間,此時整個網頁不一定要全部渲染完成,但需要展示當前視窗需要的內容,首屏載入可以說是用戶體驗中最重要的環節 二、載入慢的原因 在頁面渲染的過程,導致載入速度慢的因素 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 背景 等值查找,有數組、列表、HashMap等,已經足夠了,範圍查找,該用什麼數據結構呢?下麵介紹java中非常好用的兩個類TreeMap和ConcurrentSkipListMap。 TreeMap的實現基於紅黑樹 每一棵紅黑樹都是一顆二叉排序樹,又稱二叉查找樹(Binary Search Tre ...
  • 在大部分涉及到資料庫操作的項目裡面,事務控制、事務處理都是一個無法迴避的問題。得益於Spring框架的封裝,業務代碼中進行事務控制操作起來也很簡單,直接加個@Transactional註解即可,大大簡化了對業務代碼的侵入性。那麼對@Transactional事務註解瞭解的夠全面嗎?知道有哪些場景可能... ...
  • 寫在前面 這是我在接觸爬蟲後,寫的第二個爬蟲實例。 也是我在學習python後真正意義上寫的第二個小項目,第一個小項目就是第一個爬蟲了。 我從學習python到現在,也就三個星期不到,平時課程比較多,python是額外學習的,每天學習python的時間也就一個小時左右。 所以我目前對於python也 ...
  • 多對一關係是什麼 Django使用django.db.models.ForeignKey定義多對一關係。 ForeignKey需要一個位置參數:與該模型關聯的類 class Info(models.Model): user = models.ForeignKey(other_model,on_del ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1.講故事 在分析的眾多dump中,經常會遇到各種奇葩的問題,僅通過dump這種快照形式還是有很多問題搞不定,而通過 perfview 這種粒度又太粗,很難找到問題之所在,真的很頭疼,比如本篇的 短命線程 問題,參考圖如下: 我們在 t2 時刻抓取的dump對查看 短命線程 毫無幫助,我根 ...
  • 在日常後端Api開發中,我們跟前端的溝通中,通常需要協商好入參的數據類型,和參數是通過什麼方式存在於請求中的,是表單(form)、請求體(body)、地址欄參數(query)、還是說通過請求頭(header)。 當協商好後,我們的介面又需要怎麼去接收這些數據呢?很多小伙伴可能上手就是直接寫一個實體, ...
  • 許多情況下我們需要用到攝像頭獲取圖像,進而處理圖像,這篇博文介紹利用pyqt5、OpenCV實現用電腦上連接的攝像頭拍照並保存照片。為了使用和後續開發方便,這裡利用pyqt5設計了個相機界面,後面將介紹如何實現,要點包括界面設計、邏輯實現及完整代碼。 ...
  • 思路分析 註冊頁面需要對用戶提交的數據進行校驗,並且需要對用戶輸入錯誤的地方進行提示! 所有我們需要使用forms組件搭建註冊頁面! 平時我們書寫form是組件的時候是在views.py裡面書寫的, 但是為了接耦合,我們需要將forms組件都單獨寫在一個地方,需要用的時候導入就行! 例如,在項目文件 ...
  • 思路分析 登錄頁面,我們還是採用ajax的方式提交用戶數據 唯一需要學習的是如何製作圖片驗證碼! 具體的登錄頁面效果圖如下: 如何製作圖片驗證碼 推導步驟1:在img標簽的src屬性里放上驗證碼的請求路徑 補充1.img的src屬性: 1.圖片路徑 2.url 3.圖片的二進位數據 補充2:字體樣式 ...
  • 哈嘍,兄弟們! 最近有許多小伙伴都在吐槽打工好難。 每天都是執行許多重覆的任務 例如閱讀新聞、發郵件、查看天氣、打開書簽、清理文件夾等等, 使用自動化腳本,就無需手動一次又一次地完成這些任務, 非常方便啊有木有?! 而在某種程度上,Python 就是自動化的代名詞。 今天就來和大家一起學習一下, 用 ...
  • 作者:IT王小二 博客:https://itwxe.com 前面小二介紹過使用Typora+PicGo+LskyPro打造舒適寫作環境,那時候需要使用水印功能,但是小二在升級LskyPro2.x版本發現有很多不如人意的東西,遂棄用LskyPro使用MinIO結合代碼實現自己需要的圖床功能,也適合以後 ...
  • OpenAI Gym是一款用於研發和比較強化學習演算法的工具包,本文主要介紹Gym模擬環境的功能和工具包的使用方法,並詳細介紹其中的經典控制問題中的倒立擺(CartPole-v0/1)問題。最後針對倒立擺問題如何建立控制模型並採用爬山演算法優化進行了介紹,並給出了相應的完整python代碼示例和解釋。要... ...
  • python爬蟲瀏覽器偽裝 #導入urllib.request模塊 import urllib.request #設置請求頭 headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, l ...
  • 前端代碼搭建 主要利用的是bootstrap3中js插件里的模態框版塊 <li><a href="" data-toggle="modal" data-target=".bs-example-modal-lg">修改密碼</a></li> <div class="modal fade bs-exam ...