智能指针用法及其代码详解
網絡上大多有關智能指針的解析只停留于簡單的字面理解,今天來詳細解析一下三種智能指針的用法以及具體的代碼。
目錄
概念
RAII機制介紹
智能指針雛形
unique_ptr
weak_ptr
概念
智能指針不是一個指針,它其實是一個對象。它是通過C++的RAII機制實現的。主要是利用C++中對象在釋放的時候,會自動調用析構函數這一特性。
所以,當智能指針對象釋放的時候,在智能指針對象的析構函數中來釋放其管理的內存資源。這樣,開發人員就不需要手動去釋放已經分配的內存空間。
C++17標準之后,C++標準中還有三種智能指針:shared_ptr、unique_ptr、weak_ptr。下面我們將一一介紹。
RAII機制介紹
RAII是Resource Acquisition Is Initialization的簡稱,其翻譯過來就是“資源獲取即初始化”,即在構造函數中申請分配資源,在析構函數中釋放資源,它是C++語言中的一種管理資源、避免泄漏的良好方法。
C++語言的機制保證了,當創建一個類對象時,會自動調用構造函數,當對象超出作用域時會自動調用析構函數。RAII正是利用這種機制,利用類來管理資源,將資源與類對象的生命周期綁定,即在對象創建時獲取對應的資源,在對象生命周期內控制對資源的訪問,使之始終保持有效,最后在對象析構時,釋放所獲取的資源。
RAII技術被認為是C++中管理資源的最佳方法,更進一步來說,使用RAII技術也可以實現安全、簡潔的狀態管理。
智能指針雛形
智能指針雛形代碼:
template < typename T> class SmallSmartPtr { public: explicit SmallSmartPtr(T* ptr = nullptr): ptr_(ptr) {} ~SmallSmartPtr(){ delete ptr_; }T* get() const { return ptr_; } private:T* ptr_; };測試代碼:
class Test { public:Test(){cout << "Test()" << endl;}~Test(){cout << "~Test()" << endl;} };int main() {{SmallSmartPtr<Test> t(new Test());}system("pause"); }測試結果:
shared_ptr原理介紹
shared_ptr是智能指針的一種,不僅通過RAII機制來管理內存資源,還引入了引用計數來解決當多個智能指針指向同一塊內存空間的時候,何時釋放這塊內存空間的問題。也就是說,同一時刻可以有多個shared_ptr擁有一塊內存空間的所有權,當最后一個shared_ptr被銷毀時,這塊內存空間的引用計數為0時,這塊內存空間將被釋放。
shared_ptr對象有兩個指針,一個是指向管理的內存空間,一個是指向內存控制塊,內存控制塊中包含引用計數和其他的一些信息(刪除器和分配器)。
代碼示例:
shared_ptr<Object> t1(new Object()); shared_ptr<Object> t2 = t1;用圖表示如下:
t1釋放的時候,引用計數減一,然后釋放t1的內存空間,如下:
當t2釋放的時候,引用計數會再減一,這時引用計數就會變成0,這時就會釋放Object的內存空間和內存控制塊的空間,同時t2對象的空間也會被釋放。
shared_ptr使用方法
1)初始化
//通過構造函數初始化 shared_ptr<Test> t(new Test());//使用make_shared來初始化智能指針 shared_ptr<Test> t = make_shared<Test>();注意:不要使用裸指針進行初始化?如:
//盡量不要使用裸指針來初始化智能指針 Test* pTest = new Test(); shared_ptr<Test> t(pTest);因為使用裸指針初始化智能指針,容易導致多次使用同一個裸指針對多個智能對象進行初始化。這樣就會導致兩個智能指針在銷毀的時候會去釋放同一片內存空間。會造成程序異常崩潰。 如:
Test* pTest = new Test(); shared_ptr<Test> t(pTest);//t1釋放的時候會導致程序異常 shared_ptr<Test> t1(pTest);2)支持拷貝構造、賦值
shared_ptr<Test> t(new Test()); shared_ptr<Test> t1(t); t1 = t;3)獲取原始指針
shared_ptr<Test> t = make_shared<Test>(); Test* pTest = t.get();unique_ptr
unique_ptr是獨占型智能指針。獨占性,就是不允許多個智能指針指向同一塊內存空間。也不支持拷貝、賦值,即不能通過賦值將一個unique_ptr賦值給另一個unique_ptr。
1)初始化
//通過構造函數初始化 unique_ptr<Test> t(new Test());//使用make_unique來初始化智能指針 unique_ptr<Test> t = make_unique<Test>();2)獲取原始指針
unique_ptr<Test> t(new Test()); Test* pTest = t.get();3)支持所有權轉移
unique_ptr不支持拷貝、賦值,但支持所有權轉移,即智能指針將當前所指的內存空間的所有權交給另一個智能指針。被管理的內存空間永遠只有一個智能指針指向它。
unique_ptr<Test> t(new Test()); unique_ptr<Test> t1 = move(t);weak_ptr
weak_ptr和shared_ptr、unique_ptr不同,weak_ptr不能單獨作為智能指針使用。weak_ptr是用來輔助shared_ptr來解決循環引用的問題。
先看一個示例:
class Test; class Object { public:Object(){cout << "Object()" << endl;}~Object(){cout << "~Object()" << endl;}shared_ptr<Test> m_pTest; };class Test { public:Test(){cout << "Test()" << endl;}~Test(){cout << "~Test()" << endl;}shared_ptr<Object> m_pObject; };int main() {{shared_ptr<Object> t1(new Object());shared_ptr<Test> t2(new Test());t1->m_pTest = t2;t2->m_pObject = t1;}//離開作用域,釋放t1、t2system("pause"); }執行結果如下:
發現問題沒?為什么沒有調用兩個對象的析構函數?,也就是說,兩個智能指針釋放了,但是管理的內存空間沒有被釋放,那不就是內存泄漏嗎,我們看下這一切是怎么發生的:
這個示例的重點是,Test和Object對象中各有一個智能指針類型的成員,而且這兩個智能指針指向的都是對方的內存空間。
如圖所示,每塊內存空間都有兩個智能指針指向。當t1釋放的時候,釋放t1對象的內存空間,Test的內存塊引用計數減一:
同理,t2對象釋放的時候,釋放t2對象的內存空間,Object的內存塊引用計數減一:
此時,已經沒有智能指針管理這塊內存空間,但是這兩塊內存空間還未被釋放,這個時候,就造成了內存泄漏。
問題的根因是,Test和Object內部的成員指針指向對方的時候,也造成了引用計數加1。
要解決這個問題就需要用到兩個東西:weak_ptr和弱引用計數。
也就是說,內存控制塊中有一個特殊的引用計數,叫弱引用計數。就是上圖中的weak count。為區分,也為了方便表達,我們把原來的引用計數叫強引用計數 。
weak_ptr指向一個對象,不會造成這個對象的內存控制塊中的強引用計數加1,只會讓弱引用計數加1。而內存空間什么時候釋放,是取決于對應的強引用計數什么時候變成0。所以,當t1、t2都被釋放之后,兩個對象的強引用計數都會變成0。所以,內存空間會被釋放。
這個示例的重點是,Test和Object對象中各有一個智能指針類型的成員,而且這兩個智能指針指向的都是對方的內存空間。
class Test; class Object { public:Object(){cout << "Object()" << endl;}~Object(){cout << "~Object()" << endl;} //修改成weak_ptrweak_ptr<Test> m_pTest; };class Test { public:Test(){cout << "Test()" << endl;}~Test(){cout << "~Test()" << endl;} //修改成weak_ptrweak_ptr<Object> m_pObject; };int main() {{shared_ptr<Object> t1(new Object());shared_ptr<Test> t2(new Test());t1->m_pTest = t2;t2->m_pObject = t1;}system("pause"); }運行結果:
總結
以上是生活随笔為你收集整理的智能指针用法及其代码详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CH8-HarmonyOS流转架构解析
- 下一篇: 野牛NBIOT 环境监测项目---基于Q