目錄
1、引言
2、什么是智能指針?
3、在Visual Studio中查看智能指針的源碼實現(xiàn)
4、獨占式指針unique_ptr
4.1、查看unique_ptr的源碼實現(xiàn)片段
4.2、為什么unique_ptr的拷貝構(gòu)造函數(shù)和復(fù)制函數(shù)被delete了?(面試題)
4.3、使用unique_ptr獨占式智能指針的實例
5、共享式指針shared_ptr?
5.1、查看shared_ptr的源碼實現(xiàn)片段
5.2、shared_ptr的類圖說明
5.3、shared_ptr循環(huán)引用問題(面試題)
5.4、使用shared_ptr的實例
6、弱指針weak_ptr
6.1、查看weak_ptr的源碼實現(xiàn)片段
6.2、使用weak_ptr的實例
7、使用智能指針是否會影響到程序的執(zhí)行效率?
8、有必要學習C++11標準中的新特性
9、最后
VC++常用功能開發(fā)匯總(專欄文章列表,歡迎訂閱,持續(xù)更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++軟件異常排查從入門到精通系列教程(專欄文章列表,歡迎訂閱,持續(xù)更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++軟件分析工具案例集錦(正在更新中...)https://blog.csdn.net/chenlycly/category_12279968.htmlC/C++基礎(chǔ)與進階(正在更新中...)https://blog.csdn.net/chenlycly/category_11931267.html? ? ? ?C++11引入了unique_ptr、shared_ptr和weak_ptr三個智能指針,給我們編寫C++代碼帶來了很大的便利,今天我們就來詳細講講這三個智能指針的相關(guān)內(nèi)容。
1、引言
? ? ? ?在C++程序中,大部分異常問題都是與內(nèi)存相關(guān)的,都是內(nèi)存操作異常引起的。對于動態(tài)申請的堆內(nèi)存,C++沒有內(nèi)存回收機制,動態(tài)申請的內(nèi)存需要程序員自己去釋放。如果不去釋放,則會造成內(nèi)存泄漏。內(nèi)存泄漏是個很常見的問題,在日常開發(fā)過程中會時不時地遇到。發(fā)生內(nèi)存泄漏的代碼如果頻繁執(zhí)行,則會導(dǎo)致持續(xù)的內(nèi)存泄漏,長時間運行之后就會使得程序占用的虛擬內(nèi)存接近或超過進程的虛擬內(nèi)存上限,就會導(dǎo)致Out of memory內(nèi)存耗盡的異常,引發(fā)程序發(fā)生閃退或崩潰。
以32位程序為例,程序啟動時系統(tǒng)會給程序進程分配4GB的虛擬內(nèi)存空間,其中內(nèi)核態(tài)和用戶態(tài)內(nèi)存各占一半,即用戶態(tài)內(nèi)存為2GB,如果用戶態(tài)的代碼占用的用戶態(tài)虛擬內(nèi)存達到或超過2GB,就會觸發(fā)Out of memory內(nèi)存耗盡的異常。
? ? ? ?C++11標準引入三個智能指針:unique_ptr、shared_ptr和weak_ptr,使用這些智能指針就能很好地解決內(nèi)存泄漏的問題。
? ? ? ?在這里,給大家重點推薦一下我的兩個熱門暢銷專欄:
專欄1:(該專欄訂閱量接近350個,有很強的實戰(zhàn)參考價值,廣受好評?。?/span>
C++軟件異常排查從入門到精通系列教程(專欄文章列表,歡迎訂閱,持續(xù)更新...)https://blog.csdn.net/chenlycly/article/details/125529931
本專欄根據(jù)近幾年C++軟件異常排查的項目實踐,系統(tǒng)地總結(jié)了引發(fā)C++軟件異常的常見原因以及排查C++軟件異常的常用思路與方法,詳細講述了C++軟件的調(diào)試方法與手段,以圖文并茂的方式給出具體的實戰(zhàn)問題分析實例,帶領(lǐng)大家逐步掌握C++軟件調(diào)試與異常排查的相關(guān)技術(shù),適合基礎(chǔ)進階和想做技術(shù)提升的相關(guān)C++開發(fā)人員!
專欄中的文章都是通過項目實戰(zhàn)總結(jié)出來的,有很強的實戰(zhàn)參考價值!專欄文章還在持續(xù)更新中,預(yù)計文章篇數(shù)能更新到200篇以上!
專欄2:?
C/C++基礎(chǔ)與進階(專欄文章,持續(xù)更新中...)https://blog.csdn.net/chenlycly/category_11931267.html
以多年的開發(fā)實戰(zhàn)為基礎(chǔ),總結(jié)并講解一些的C/C++基礎(chǔ)與進階內(nèi)容,以圖文并茂的方式對相關(guān)知識點進行詳細地展開與闡述!專欄涉及了C/C++領(lǐng)域的多個方面的內(nèi)容,同時給出C/C++及網(wǎng)絡(luò)方面的常見筆試面試題,并詳細講述Visual Studio常用調(diào)試手段與技巧!
2、什么是智能指針?
? ? ? ?其實早在1998年發(fā)布的C++98標準中就引入了智能指針auto_ptr,不過auto_ptr有一些缺陷,現(xiàn)在已經(jīng)被廢棄了。
? ? ? ?C++智能指針是存儲指向動態(tài)分配(堆)對象指針的類,用于C++類對象的生存期的控制,確保在離開指針所在作用域時,自動地銷毀動態(tài)分配的對象,防止內(nèi)存泄露。智能指針的核心實現(xiàn)技術(shù)是引用計數(shù),每引用C++對象1次,內(nèi)部引用計數(shù)就加1;智能指針每析構(gòu)1次,內(nèi)部的引用計數(shù)就減1,當引用計數(shù)減為0時,就會刪除指向的C++類對象(釋放類對象的堆內(nèi)存)。
? ? ? ? C++11標準引入三個智能指針unique_ptr、shared_ptr和weak_ptr,位于C++標準STL庫中,在 <memory> 頭文件中的 std 命名空間中定義的。
這個地方需要注意一下,有的朋友可能會以為STL只包含vector、list和map等容器,其實大家平常使用的字符串類string、輸入輸出iostream、unique_ptr等智能指針,都是STL標準模板庫中的。STL模板庫不僅僅包含容器和迭代器。C++標準庫主要由C庫、C++庫和STL標準模板庫構(gòu)成,其中STL標準模板庫在C++標準庫中比重占了80%左右,即C++標準庫中一大半都是STL庫。
? ? ? ?C++11標準中主要使用unique_ptr和shared_ptr兩智能指針,weak_ptr則主要用來輔助shared_ptr,避免出現(xiàn)循環(huán)引用問題的。在使用unique_ptr和shared_ptr兩個智能指針類時,可以使用指針運算符(-> 和 *)訪問指向的對象,因為智能指針類重載了->和*運算符,以返回指向的對象(指針)。
_NODISCARD add_lvalue_reference_t<_Ty> operator*() const
?? ?{?? ?// return reference to object
?? ??? ?return (*get());
?? ?}
_NODISCARD pointer operator->() const noexcept
?? ?{?? ?// return pointer to class object
?? ??? ?return (this->_Myptr());
?? ?}
_NODISCARD pointer get() const noexcept
?? ?{?? ?// return pointer to object
?? ??? ?return (this->_Myptr());
?? ?}
3、在Visual Studio中查看智能指針的源碼實現(xiàn)
? ? ? ? C++11引入的unique_ptr、shared_ptr和weak_ptr,可以直接在Visual Studio中查看源碼實現(xiàn)。這三個智能指針位于STL庫中,而Visual Studio采用的是P.J. STL版本。
? ? ? ?注意一下,要在Visual Studio中查看unique_ptr和shared_ptr實現(xiàn)源碼,需要使用Visual Studio 2015或以上版本,低版本的Visual Studio可能不支持C++11標準或者支持了部分C++11新特性。比如Visual Studio 2010中只支持了部分C++11的新特性,比如匿名函數(shù)(lamda表達式),但不支持unique_ptr和shared_ptr智能指針(會顯示未定義)。
? ? ? ?目前,STL主要有5個版本:
1)HP 原始版本
? ? ? ?HP STL 是 Alexandar Stepanov(STL 標準模板庫之父)在惠普 Palo Alto 實驗室工作時,與 Meng Lee 合作完成的。本著開源精神,他們聲明允許任何人任意運用、拷貝、修改、傳播、商業(yè)使用這些代碼,無需付費。唯一要遵守的是,必修在文件中加上HP的版本聲明和運用權(quán)限聲明。?
2)P. J. 實現(xiàn)版本
? ? ? ?由 P. J. Plauger 開發(fā),繼承自 HP 版本,該版本不開源,不能公開、修改或販賣。該版本不開源也是合法的,因為HP沒要求強迫要求其衍生產(chǎn)品必須開源。該版本被微軟 Visual C++ 采用,缺陷是,可讀性比較低,符號命名也比較怪異。但我們在Visual Studio中閱讀該版本的實現(xiàn)源碼時,感覺還好,也沒傳說中那么難讀。
3)RW 實現(xiàn)版本
? ? ? ?由 Rouge Wage 公司開發(fā),繼承自 HP 版本,被Borland公司的 C+ + Builder 采用。該版本也不是開源的,不能公開、修改或販賣,這個版本的可讀性還不錯。
由 Rouge Wage 公司開發(fā),繼承自 HP 版本,被Borland公司的 C+ + Builder 采用。該版本也不是開源的,不能公開、修改或販賣,這個版本的可讀性還不錯。值得一提的是,盡管 Rouge Wave STL 的性能不是很好,但 C++ Builder 對 C++ 語言標準的支持還算不錯,所以在一定程度上使 Rouge Wave STL 的表現(xiàn)得以改善。但遺憾的是,由于 Rouge Wave STL 長期沒有更新且不完全符合標準,因此 Rouge Wave STL 在 6.0 版本時改用了 STLport 版本(之后的版本也都采用了 STLport),不過考慮到和之前版本的兼容,6.0 版本中依舊保留了 Rouge Wave STL。
4)SGI 實現(xiàn)版本
? ? ? ? 由 Silicon Graphics Computer Systems,Inc 公司開發(fā),繼承自 HP 版本。該版本被 Linux GCC 采用,可移植性好, 在 Linux 平臺上的性能非常出色。該版本是開源的,可公開、修改甚至販賣。 無論是符號命名,還是編程風格,這個版本的可讀性非常高。如果大家要學習 STL源碼,推薦大家看這個版本的源碼實現(xiàn)。侯捷老師的經(jīng)典書籍《STL源碼剖析》,也是基于這個版本展開的。
5)STLport 實現(xiàn)版本
? ? ? ? 為了使 SGI STL 的基本代碼都適用于 VC++ 和 C++ Builder 等多種編譯器,俄國人 Boris Fomitchev 建立了一個 free 項目來開發(fā) STLport,此版本 STL 是開放源碼的。由于 Rouge Wave STL 長期沒有更新且不完全符合標準,因此 Rouge Wave STL 在 6.0 版本時改用了 STLport 版本,之后的版本也都采用了 STLport。
4、獨占式指針unique_ptr
? ? ? ? unique_ptr是獨占式智能指針,它擁有對其所指向?qū)ο蟮奈ㄒ凰袡?quán)。unique_ptr 指針被銷毀時,它所指向的對象也會被銷毀。由于其具有獨占性,所以unique_ptr 不能被拷貝,只能被轉(zhuǎn)移所有權(quán)。將對象的所有權(quán)轉(zhuǎn)移到新的unique_ptr對象中,原先的unique_ptr對象不再指向原來的對象。
4.1、查看unique_ptr的源碼實現(xiàn)片段
? ? ? ? 可以在Visual Studio中輸入unique_ptr,然后go到unique_ptr的定義處查看unique_ptr的源碼實現(xiàn),都是基于模板實現(xiàn)的:(下面給出部分unique_ptr的源碼)
?? ?// CLASS TEMPLATE unique_ptr SCALAR
template<class _Ty,
?? ?class _Dx>?? ?// = default_delete<_Ty>
?? ?class unique_ptr
?? ??? ?: public _Unique_ptr_base<_Ty, _Dx>
?? ?{?? ?// non-copyable pointer to an object
public:
?? ?typedef _Unique_ptr_base<_Ty, _Dx> _Mybase;
?? ?typedef typename _Mybase::pointer pointer;
?? ?typedef _Ty element_type;
?? ?typedef _Dx deleter_type;
?? ?using _Mybase::get_deleter;
?? ?template<class _Dx2 = _Dx,
?? ??? ?_Unique_ptr_enable_default_t<_Dx2> = 0>
?? ??? ?constexpr unique_ptr() noexcept
?? ??? ??? ?: _Mybase(pointer())
?? ??? ?{?? ?// default construct
?? ??? ?}
?? ?template<class _Dx2 = _Dx,
?? ??? ?_Unique_ptr_enable_default_t<_Dx2> = 0>
?? ??? ?constexpr unique_ptr(nullptr_t) noexcept
?? ??? ??? ?: _Mybase(pointer())
?? ??? ?{?? ?// null pointer construct
?? ??? ?}
?? ?unique_ptr& operator=(nullptr_t) noexcept
?? ??? ?{?? ?// assign a null pointer
?? ??? ?reset();
?? ??? ?return (*this);
?? ??? ?}
?? ?template<class _Dx2 = _Dx,
?? ??? ?_Unique_ptr_enable_default_t<_Dx2> = 0>
?? ??? ?explicit unique_ptr(pointer _Ptr) noexcept
?? ??? ??? ?: _Mybase(_Ptr)
?? ??? ?{?? ?// construct with pointer
?? ??? ?}
?? ??? ?
?? ??? ?// ...(余下源碼省略)
?? ?}
源碼就不在此解讀了,感興趣可以去詳細看看。源碼中到處都是模板,需要有一定的模板編程基礎(chǔ)才能看懂。
4.2、為什么unique_ptr的拷貝構(gòu)造函數(shù)和復(fù)制函數(shù)被delete了?(面試題)
? ? ? ?現(xiàn)在C++崗位面試時,很多公司都喜歡問C++11新標準中的若干特性,所以大家在面試前需要詳細看看C++11新特性。
? ? ? ?之前有個同事出去面試時,就被問到為什么unique_ptr的拷貝構(gòu)造函數(shù)和復(fù)制函數(shù)被delete了?在unique_ptr的實現(xiàn)代碼中可以看到:
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
其實很簡單,因為unique_ptr是獨占式的,不能進行拷貝,只能進行對象所有權(quán)的轉(zhuǎn)移??截悩?gòu)造函數(shù)與賦值函數(shù)進行的是拷貝操作,所以要將這兩個函數(shù)禁用掉。
注意此處在函數(shù)后面添加“= delete”,是C++11新標準中新增的,是用來禁用對應(yīng)的函數(shù)的。
4.3、使用unique_ptr獨占式智能指針的實例
? ? ? ? 使用unique_ptr智能指針的簡單示例如下:?
#include <iostream>
#include <memory>
class LargeObject
{
public:
? ? void DoSomething(){}
};
void SmartPointerDemo()
{
? ? // Create the object and pass it to a smart pointer
? ? std::unique_ptr<LargeObject> pLarge(new LargeObject());
? ? //Call a method on the object
? ? pLarge->DoSomething();
? ? // Free the memory before we exit function block.
? ? pLarge.reset();
? ? // Do some other work...
}
5、共享式指針shared_ptr?
? ? ? ? shared_ptr是共享式智能指針,一個對象可以被多個shared_ptr共享。每個shared_ptr內(nèi)部都維護一個引用計數(shù),當引用計數(shù)為 0 時,所指向的對象會被銷毀。std::shared_ptr 可以被拷貝和移動。
5.1、查看shared_ptr的源碼實現(xiàn)片段
? ? ? ?可以在Visual Studio中輸入shared_ptr,然后go到shared_ptr的定義處查看shared_ptr的源碼實現(xiàn),都是基于模板實現(xiàn)的:(下面給出部分shared_ptr的源碼)
?? ?// CLASS TEMPLATE shared_ptr
template<class _Ty>
?? ?class shared_ptr
?? ??? ?: public _Ptr_base<_Ty>
?? ?{?? ?// class for reference counted resource management
private:
?? ?using _Mybase = _Ptr_base<_Ty>;
public:
?? ?using typename _Mybase::element_type;
#if _HAS_CXX17
?? ?using weak_type = weak_ptr<_Ty>;
#endif /* _HAS_CXX17 */
?? ?constexpr shared_ptr() noexcept
?? ??? ?{?? ?// construct empty shared_ptr
?? ??? ?}
?? ?constexpr shared_ptr(nullptr_t) noexcept
?? ??? ?{?? ?// construct empty shared_ptr
?? ??? ?}
?? ?template<class _Ux,
?? ??? ?enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
?? ??? ??? ?_SP_convertible<_Ux, _Ty>>, int> = 0>
?? ??? ?explicit shared_ptr(_Ux * _Px)
?? ??? ?{?? ?// construct shared_ptr object that owns _Px
?? ??? ?_Setp(_Px, is_array<_Ty>{});
?? ??? ?}
?? ?template<class _Ux,
?? ??? ?class _Dx,
?? ??? ?enable_if_t<conjunction_v<is_move_constructible<_Dx>,
?? ??? ??? ?_Can_call_function_object<_Dx&, _Ux *&>,
?? ??? ??? ?_SP_convertible<_Ux, _Ty>>, int> = 0>
?? ??? ?shared_ptr(_Ux * _Px, _Dx _Dt)
?? ??? ?{?? ?// construct with _Px, deleter
?? ??? ?_Setpd(_Px, _STD move(_Dt));
?? ??? ?}
?? ?template<class _Ux,
?? ??? ?class _Dx,
?? ??? ?class _Alloc,
?? ??? ?enable_if_t<conjunction_v<is_move_constructible<_Dx>,
?? ??? ??? ?_Can_call_function_object<_Dx&, _Ux *&>,
?? ??? ??? ?_SP_convertible<_Ux, _Ty>>, int> = 0>
?? ??? ?shared_ptr(_Ux * _Px, _Dx _Dt, _Alloc _Ax)
?? ??? ?{?? ?// construct with _Px, deleter, allocator
?? ??? ?_Setpda(_Px, _STD move(_Dt), _Ax);
?? ??? ?}
?? ?template<class _Dx,
?? ??? ?enable_if_t<conjunction_v<is_move_constructible<_Dx>,
?? ??? ??? ?_Can_call_function_object<_Dx&, nullptr_t&>
?? ??? ?>, int> = 0>
?? ??? ?shared_ptr(nullptr_t, _Dx _Dt)
?? ??? ?{?? ?// construct with nullptr, deleter
?? ??? ?_Setpd(nullptr, _STD move(_Dt));
?? ??? ?}
?? ?template<class _Dx,
?? ??? ?class _Alloc,
?? ??? ?enable_if_t<conjunction_v<is_move_constructible<_Dx>,
?? ??? ??? ?_Can_call_function_object<_Dx&, nullptr_t&>
?? ??? ?>, int> = 0>
?? ??? ?shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)
?? ??? ?{?? ?// construct with nullptr, deleter, allocator
?? ??? ?_Setpda(nullptr, _STD move(_Dt), _Ax);
?? ??? ?}
?? ?template<class _Ty2>
?? ??? ?shared_ptr(const shared_ptr<_Ty2>& _Right, element_type * _Px) noexcept
?? ??? ?{?? ?// construct shared_ptr object that aliases _Right
?? ??? ?this->_Alias_construct_from(_Right, _Px);
?? ??? ?}
?? ?shared_ptr(const shared_ptr& _Other) noexcept
?? ??? ?{?? ?// construct shared_ptr object that owns same resource as _Other
?? ??? ?this->_Copy_construct_from(_Other);
?? ??? ?}
?? ??? ?
?? ??? ?// ...(余下源碼省略)
}
5.2、shared_ptr的類圖說明
? ? ? ?shared_ptr類內(nèi)部實現(xiàn)類圖如下:
shared_ptr類繼承于_Ptr_base類,_Ptr_base類內(nèi)部包含了指向外部對象的成員_Ptr和_Ref_count_base計數(shù)對象。根據(jù)外部調(diào)用shared_ptr的哪個構(gòu)造函數(shù),確定到底是new出_Ref_count_obj、_Ref_count或者_Ref_count_resource中哪個計數(shù)對象。類圖很重要,類圖可以表明各個相關(guān)類的關(guān)系,無論是寫設(shè)計文檔,還是學習源碼,都需要使用到類圖。
5.3、shared_ptr循環(huán)引用問題(面試題)
? ? ? ?使用shared_ptr可能會出現(xiàn)循環(huán)引用問題,場景是兩個類中都包含了指向?qū)Ψ降膕hared_ptr對象,這樣會導(dǎo)致new出來的兩個類沒有走析構(gòu),引發(fā)內(nèi)存泄漏問題。
? ? ? ?循環(huán)引用問題的示意圖如下:
相關(guān)代碼如下:
#include <iostream>
#include<memory>
?
using namespace std;
?
class B;
class A{
public:
? ? shared_ptr<B> bptr;
? ? ~A(){cout<<"~A()"<<endl;}
}
?
class B
{
public:
? ? shared_ptr<A> aptr;
? ? ~B( ){cout<<"~B()"<<endl;}
}
int main() {
? ? shared_ptr<A> pa(new A()); // 引用加1
? ? shared_ptr<B> pb(new B()); // 引用加1
? ? pa->bptr = pb; // 引用加1
? ? pa->aptr = pa; // 引用加1
? ? return 0;
}
? ? ? ?執(zhí)行到上述return 0這句代碼時,指向A和B兩個對象的引用計數(shù)都是2。當退出main函數(shù)時,先析構(gòu)shared_ptr<B> pb對象,B對象的引用計數(shù)減1,B對象的引用計數(shù)還為1,所以不會delete B對象,不會進入B對象析構(gòu)函數(shù),所以B類中的shared_ptr<A> aptr成員不會析構(gòu),所以此時A對象的引用計數(shù)還是2。當析構(gòu)shared_ptr<A> pa時,A的引用計數(shù)減1,A對象的引用計數(shù)變?yōu)?,所以不會析構(gòu)A對象。所以上述代碼會導(dǎo)致A和B兩個new出的對象都沒釋放,導(dǎo)致內(nèi)存泄漏。
? ? ? ?為了解決上述問題,引入了weak_ptr,可以將類中包含的shared_ptr成員換成weak_ptr,如下:
相關(guān)代碼如下:
#include <iostream>
#include<nemory>
?
using namespace std;
?
class B;
class A{
public:
? ? weak_ptr<B> bptr; ?// 使用weak_ptr替代shared_ptr
? ? ~A(){cout<<"~A()"<<endl;}
}
?
class B
{
public:
? ? weak_ptr<A> aptr; // 使用weak_ptr替代shared_ptr
? ? ~B( ){cout<<"~B()"<<endl;}
}
int main() {
? ? shared_ptr<A> pa(new A());
? ? shared_ptr<B> pb(new B());
? ? pa->bptr = pb;
? ? pa->aptr = pa;
? ? return 0;
}
5.4、使用shared_ptr的實例
? ? ? ? 使用shared_ptr的實例代碼如下:
#include <iostream>
#include <memory>
?
int main()?
{?
? ? std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(10);
? ? std::shared_ptr<int> sharedPtr2 = sharedPtr1;
? ? std::cout << *sharedPtr1 << std::endl; // 輸出 10
? ? std::cout << *sharedPtr2 << std::endl; // 輸出 10
?
? ? // 使用智能指針自動管理內(nèi)存,不需要手動釋放
? ? // 智能指針會自動更新引用計數(shù)
? ? sharedPtr1.reset();
?
? ? // 只有當所有引用都被釋放后,內(nèi)存才會被自動釋放
? ? std::cout << "sharedPtr2 use count: " << sharedPtr2.use_count() << std::endl; // 輸出 1
}
6、弱指針weak_ptr
? ? ? ? weak_ptr是為了配合shared_ptr而引入的一種智能指針,它不具有普通指針的行為,沒有重載*和->兩個操作符,它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。
? ? ? ? weak_ptr可以從一個shared_ptr或者另一個weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)。但weak_ptr沒有共享資源,它的構(gòu)造不會引起指針引用計數(shù)的增加。使用weak_ptr的成員函數(shù)use_count()可以觀測資源的引用計數(shù),另一個成員函數(shù)expired()的功能等價于use_count()==0,但更快,表示被觀測的資源 (也就是shared_ptr的管理的資源)已經(jīng)不復(fù)存在。weak_ptr可以使用一個非常重要的成員函數(shù)lock()從被觀測的shared_ptr 獲得一個可用的shared_ptr對象,從而操作資源。但當expired()==true的時候,lock()函數(shù)將返回一個存儲空指針的shared_ptr。
6.1、查看weak_ptr的源碼實現(xiàn)片段
? ? ? ?到Visual Studio中可以看到weak_ptr的源碼實現(xiàn):
// CLASS TEMPLATE weak_ptr
template<class _Ty>
?? ?class weak_ptr
?? ??? ?: public _Ptr_base<_Ty>
?? ?{?? ?// class for pointer to reference counted resource
public:
?? ?constexpr weak_ptr() noexcept
?? ??? ?{?? ?// construct empty weak_ptr object
?? ??? ?}
?? ?weak_ptr(const weak_ptr& _Other) noexcept
?? ??? ?{?? ?// construct weak_ptr object for resource pointed to by _Other
?? ??? ?this->_Weakly_construct_from(_Other);
?? ??? ?}
?? ?template<class _Ty2,
?? ??? ?enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
?? ??? ?weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept
?? ??? ?{?? ?// construct weak_ptr object for resource owned by _Other
?? ??? ?this->_Weakly_construct_from(_Other);
?? ??? ?}
?? ?template<class _Ty2,
?? ??? ?enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
?? ??? ?weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept
?? ??? ?{?? ?// construct weak_ptr object for resource pointed to by _Other
?? ??? ?this->_Weakly_construct_from(_Other.lock());
?? ??? ?}
?? ?weak_ptr(weak_ptr&& _Other) noexcept
?? ??? ?{?? ?// move construct from _Other
?? ??? ?this->_Move_construct_from(_STD move(_Other));
?? ??? ?}
?? ?template<class _Ty2,
?? ??? ?enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
?? ??? ?weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept
?? ??? ?{?? ?// move construct from _Other
?? ??? ?this->_Weakly_construct_from(_Other.lock());
?? ??? ?_Other.reset();
?? ??? ?}
?? ?~weak_ptr() noexcept
?? ??? ?{?? ?// release resource
?? ??? ?this->_Decwref();
?? ??? ?}
?? ?weak_ptr& operator=(const weak_ptr& _Right) noexcept
?? ??? ?{?? ?// assign from _Right
?? ??? ?weak_ptr(_Right).swap(*this);
?? ??? ?return (*this);
?? ??? ?}
?? ?template<class _Ty2>
?? ??? ?weak_ptr& operator=(const weak_ptr<_Ty2>& _Right) noexcept
?? ??? ?{?? ?// assign from _Right
?? ??? ?weak_ptr(_Right).swap(*this);
?? ??? ?return (*this);
?? ??? ?}
?? ??? ?
?? ??? ?// ...(余下源碼省略)
}
6.2、使用weak_ptr的實例
#include <iostream> ?
using namespace std; ?
#include <memory> ?
??
class B; ?
??
class A ?
{ ?
public: ?
? ? weak_ptr<B> ptrA_B; ?// 弱類型指針
public: ?
? ? A() ?
? ? { ?
? ? ? ? cout << "調(diào)用class A的構(gòu)造函數(shù)" << endl; ?
? ? } ?
? ? ~A() ?
? ? { ?
? ? ? ? cout << "調(diào)用class A的析構(gòu)函數(shù)" << endl; ?
? ? } ?
}; ?
??
class B ?
{ ?
public: ?
? ? weak_ptr<A> ptrB_A; ?// 弱類型指針
public: ?
? ? B() ?
? ? { ?
? ? ? ? cout << "調(diào)用class B的構(gòu)造函數(shù)" << endl; ?
? ? } ?
? ? ~B() ?
? ? { ?
? ? ? ? cout << "調(diào)用class B的析構(gòu)函數(shù)" << endl; ?
? ? } ?
}; ?
??
int main() ?
{ ?
?? ?shared_ptr<A> ptrA = make_shared<A>();
? ? shared_ptr<B> ptrB = make_shared<B>(); ?
? ? ptrA->ptrA_B = ptrB; ?
? ? ptrB->ptrB_A = ptrA; ?
} ?
上述代碼運行的輸出結(jié)果是:
調(diào)用class B的構(gòu)造函數(shù)
調(diào)用class A的構(gòu)造函數(shù)
調(diào)用class A的析構(gòu)函數(shù)
調(diào)用class B的析構(gòu)函數(shù)
7、使用智能指針是否會影響到程序的執(zhí)行效率?
? ? ? ?使用智能指針不會影響到程序的執(zhí)行效率。智能指針的設(shè)計原則是在內(nèi)存和性能上盡可能高效。 例如,unique_ptr中的唯一數(shù)據(jù)成員是封裝的指針。 從內(nèi)存占用上看,unique_ptr 與該指針的大小完全相同,不是四個字節(jié)就是八個字節(jié)。從性能上看,使用重載了 * 和 ->運算符的智能指針訪問封裝指針的速度不會明顯慢于直接訪問原始指針的速度。
8、有必要學習C++11標準中的新特性
? ? ? ?2011年發(fā)布的C++11新標準,給C++引入了大量的新特性,是C++發(fā)展史上一次里程碑式的更新,開啟了現(xiàn)代C++的時代!unique_ptr、shared_ptr和weak_ptr智能指針就是在這次更新中引入的!
? ? ? ?為什么說我們很有必要學習C++11標準中的新特性呢?主要從兩點來看。一方面,現(xiàn)在很多公司在招聘C++開發(fā)人員時會頻繁地問到C++11的新特性;另一方面,很多開源代碼在頻繁地使用C++11的新特性,比如很多公司都在用的開源WebRTC庫,就大量地使用到了C++11及C++14中的新特性,我們要閱讀這些開源代碼,必須要了解C++新特性。
? ? ? ?C++新標準引入的新特性,使得C++變得更加靈活,但也使得C++變得更加臃腫,更加難以駕馭!C++這些新特性,能真正駕馭的人并不多,所以為了保證代碼的可讀性與可維護性,在一般公司的項目代碼中C++新特性用的并不多,一般只會用到少部分特性,比如一些新增的關(guān)鍵字和匿名函數(shù)(lamda表達式)等。文章來源:http://www.zghlxwxcb.cn/news/detail-473857.html
9、最后
? ? ? ?本文詳細地介紹了C++11標準中引入的智能指針unique_ptr、shared_ptr和weak_ptr基礎(chǔ)部分的內(nèi)容,希望能給大家提供的一定的借鑒和參考。后面將介紹在代碼中如何有效地使用這些智能指針。文章來源地址http://www.zghlxwxcb.cn/news/detail-473857.html
到了這里,關(guān)于C++11中的智能指針unique_ptr、shared_ptr和weak_ptr詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!