C++基本用法實踐之智能指針詳解
概述
c++效率較高的一個原因是我們可以自己定制策略手動申請和釋放內(nèi)存,當(dāng)然,也伴隨著開發(fā)效率降低和內(nèi)存泄漏的風(fēng)險。為了減少手動管理內(nèi)存帶來的困擾,c++提出了智能指針,可以幫助我們進行內(nèi)存管理,有三種:
std::unique_ptr
是一種獨占所有權(quán)的智能指針,它不允許多個指針指向同一個對象。std::unique_ptr
是一種輕量級的智能指針,不需要額外的開銷,因此在你不需要共享所有權(quán)的情況下,應(yīng)優(yōu)先使用std::unique_ptr
。std::shared_ptr
是一種可以共享所有權(quán)的智能指針。多個std::shared_ptr
可以指向同一個對象,該對象只有在最后一個指向它的std::shared_ptr
被銷毀時才會被刪除。std::shared_ptr
使用引用計數(shù)來跟蹤有多少個智能指針指向同一個對象。std::weak_ptr
是一種弱引用智能指針,它可以指向std::shared_ptr
所管理的對象,但它不會增加該對象的引用計數(shù)。這對于解決std::shared_ptr
的循環(huán)引用問題很有用。
智能指針的類型決定了何時銷毀和釋放內(nèi)存。當(dāng)智能指針的生命周期結(jié)束時,它會自動清理其所有權(quán)的內(nèi)存。這就避免了內(nèi)存泄漏,使代碼更安全、更健壯。
但是智能指針的使用,也要注意一些問題:
- 智能指針并不能自動解決循環(huán)引用的問題,需要手動在合適的場合使用
std::weak_ptr
弱指針 - 智能指針的使用是“傳染性”的,如果使用智能指針,要考慮好整個邏輯鏈條都使用智能指針而不是原生的指針
- 從實際項目使用上看,一些比較上層的模塊使用智能指針,更加方便,而在底層模塊更多的還是使用原生的指針,我想大概因為智能指針也是會帶來一些開銷而且可能和一些內(nèi)存管理策略沖突(如某些優(yōu)化內(nèi)存策略可能不使用指針,而使用句柄來指向?qū)ο螅?/li>
智能指針是一個對象,實現(xiàn)是非入侵式的。關(guān)于他們的大概的原理:
std::unique_ptr
它包含一個原生指針,并在其析構(gòu)函數(shù)中刪除這個指針。std::unique_ptr
還重載了->
和*
運算符,所以可以像使用原生指針一樣使用std::unique_ptr
std::shared_ptr
的實現(xiàn)則相對復(fù)雜一些。除了存儲原生指針,std::shared_ptr
還需要存儲一個引用計數(shù)。每次創(chuàng)建一個新的std::shared_ptr
或者調(diào)用std::shared_ptr
的拷貝構(gòu)造函數(shù)或賦值運算符時,引用計數(shù)就會增加。每次銷毀一個std::shared_ptr
時,引用計數(shù)就會減少。只有當(dāng)引用計數(shù)降到0時,才會刪除原生指針。類似的還要處理若引用。此外,引用計數(shù)還要保證線程安全。
用法舉例
參考測試項目代碼ModernCppTest/modrenc_smart_pointer.cpp
主要包含:
- 共享指針用法&聲明方式
- 獨占指針用法&聲明方式
- 弱指針的用法
#include "ModernCppTestHeader.h" #include <memory> #define LOG_UNIQUE_PTR_VALID(ptr) if(ptr) LOG(#ptr << " unique_ptr valid"); else LOG(#ptr << " unique_ptr invalid") #define LOG_WEAK_PTR_IF_VALID(ptr, exp) if(auto tp = p2.lock()) exp else LOG(#ptr << " weak_ptr invalid") namespace n_smart_pointer { class Obj { public: Obj(int ID) : ID(ID) { LOG("Obj addr " << this << " ID " << ID << " Create"); } ~Obj() { LOG("Obj addr " << this << " ID " << ID << " Release"); } int ID; }; class Item {}; } using LocaObj = n_smart_pointer::Obj; void smart_pointer_test() { LOG_FUNC(); LOG_TAG("std::shared_ptr 共享指針"); { { std::shared_ptr<LocaObj> p1 = std::make_shared<LocaObj>(1); { std::shared_ptr<LocaObj> p2 = p1; LOG_VAR_DESC(p1->ID, " p1->ID"); LOG_VAR_DESC(p2->ID, " p2->ID"); LOG("p2 離開作用域"); } LOG("p1 離開作用域"); } } LOG_TAG("std::shared_ptr 共享指針的聲明方式"); { std::shared_ptr<LocaObj> p1(new LocaObj(1)); std::shared_ptr<LocaObj> p2 = std::make_shared<LocaObj>(2); std::shared_ptr<LocaObj> p3(p2); } LOG_TAG("std::unique_ptr 唯一指針"); { { std::unique_ptr<LocaObj> p1; { std::unique_ptr<LocaObj> p2 = std::make_unique<LocaObj>(1); LOG_VAR_DESC(p2->ID, " p2->ID"); LOG("唯一指針轉(zhuǎn)移控制權(quán)使用=運算符報錯,須使用std::move()"); // std::unique_ptr<LocaObj> p1 = p2; p1 = std::move(p2); LOG_VAR_DESC(p1->ID, " p1->ID"); LOG("測試被std::move的p2是否還有效,可以看到 無效"); LOG_UNIQUE_PTR_VALID(p2); LOG("p2 離開作用域, ID為1的Obj沒有析構(gòu)"); } LOG("p1 離開作用域, ID為1的Obj析構(gòu)"); } } LOG_TAG("std::unique_ptr 唯一指針的聲明方式"); { std::unique_ptr<LocaObj> p1(new LocaObj(1)); std::unique_ptr<LocaObj> p2 = std::make_unique<LocaObj>(2); std::unique_ptr<LocaObj> p3(std::move(p2)); } LOG_TAG("std::weak_ptr 弱指針"); { std::shared_ptr<LocaObj> p1 = std::make_shared<LocaObj>(1); std::weak_ptr<LocaObj> p2 = p1; LOG_VAR_DESC(p1->ID, " p1->ID"); LOG_WEAK_PTR_IF_VALID(p2, LOG_VAR_DESC(tp->ID, " p2->ID");); LOG("p1 reset Obj對象被釋放"); p1.reset(); LOG("p2 弱指針失效"); LOG_WEAK_PTR_IF_VALID(p2, LOG_VAR_DESC(tp->ID, " p2->ID");); } }
到此這篇關(guān)于C++基本用法實踐之智能指針詳解的文章就介紹到這了,更多相關(guān)C++智能指針內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用VScode搭建ROS開發(fā)環(huán)境的教程詳解
這篇文章主要介紹了使用VScode搭建ROS開發(fā)環(huán)境,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08C++實現(xiàn)LeetCode(52.N皇后問題之二)
這篇文章主要介紹了C++實現(xiàn)LeetCode(52.N皇后問題之二),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07