C++如何用智能指針管理內(nèi)存資源
1.簡介
C++作為一門應(yīng)用廣泛的高級編程語言,卻沒有像Java、C#等語言擁有垃圾回收(Garbage Collection )機制來自動進行內(nèi)存管理,這也是C++一直被詬病的一點。C++在發(fā)展的過程中,一直致力于解決內(nèi)存泄漏,C++雖然基于效率的考慮,沒有采用垃圾回收機制,但從C++98開始,推出了智能指針(Smart Pointer)來管理內(nèi)存資源,以彌補C++在內(nèi)存管理上的技術(shù)空白。
智能指針是C++程序員們一件管理內(nèi)存的利器,使用智能指針管理內(nèi)存資源,實際上就是將申請的內(nèi)存資源交由智能指針來管理,是RAII技術(shù)的一種實現(xiàn)。RAII是C++的之父Bjarne Stroustrup教授提出的概念,RAII全稱是“Resource Acquisition is Initialization”,直譯過來是“資源獲取即初始化”,也就是說在構(gòu)造函數(shù)中獲取資源,在析構(gòu)函數(shù)中釋放資源。因為C++的語言機制保證了,當(dāng)一個對象創(chuàng)建的時候,自動調(diào)用構(gòu)造函數(shù),當(dāng)對象超出作用域的時候會自動調(diào)用析構(gòu)函數(shù)。所以,在RAII的指導(dǎo)下,我們應(yīng)該使用類來管理資源,將資源和對象的生命周期綁定。
“資源獲取即初始化”,在使用智能指針管理內(nèi)存資源時,“資源”指的是通過new或malloc申請的內(nèi)存資源,“初始化”指的是使用申請的內(nèi)存資源來初始化棧上的智能指針類對象。使用智能指針管理內(nèi)存資源的好處顯而易見,通過智能指針對象在聲明周期結(jié)束時,自動調(diào)用析構(gòu)函數(shù),在析構(gòu)函數(shù)中完成對內(nèi)存資源的釋放,即自動的調(diào)用內(nèi)存資源的釋放代碼,避免因忘記對內(nèi)存資源的釋放導(dǎo)致內(nèi)存泄漏。
2.實例
下面看一個使用由C++11引入的智能指針unique_ptr來管理內(nèi)存資源的例子。
#include <memory> #include <iostream> using namespace std; class A { public: A() {} ~A() { cout<<"A's destructor"<<endl; } void Hello() { cout<<"use smart pointer to manage memory resources as far as possible"<<endl; } }; int main() { unique_ptr<A> pA(new A); pA->Hello(); return 0; }
程序輸出:
use smart pointer to manage memory resources as far as possible
A's destructor
可見在main()函數(shù)結(jié)束后,類A的析構(gòu)函數(shù)被自動調(diào)用,完成了內(nèi)存資源了釋放。
在創(chuàng)建智能指針對象時,也可以暫時不指定內(nèi)存資源,先創(chuàng)建一個空的智能指針對象。空智能指針對象不可以進行任何操作,但可以使用 get() 成員函數(shù)來判斷是否存在內(nèi)存資源,如果為空則可以指定內(nèi)存資源。類似于如下操作:
unique_ptr<int> pInt; if (pInt.get()==nullptr) { pInt.reset(new int(8)); cout<<*pInt<<endl; }
使用 unique_ptr 智能指針來管理內(nèi)存資源時,是對內(nèi)存資源的獨占式管理,即內(nèi)存資源的所有權(quán)不能進行共享,同一時刻只能有一個 unique_ptr 對象占有某個內(nèi)存資源。如果發(fā)生賦值或拷貝構(gòu)造,則會在編譯期報錯,因為unique_ptr禁止了拷貝語義,提高了代碼的安全性。
unique_ptr<int> pInt(new int(8)); unique_ptr<int> pInt1=pInt; //編譯報錯 unique_ptr<int> pInt2(pInt); //編譯報錯
當(dāng)然,可以通過移動語義完成內(nèi)存資源的所有權(quán)轉(zhuǎn)移,轉(zhuǎn)移之后,原智能指針對象變?yōu)榭罩悄苤羔槍ο螅荒茉賹?nèi)存資源進行任何操作,否則會發(fā)生運行時錯誤,但我們也可以使用get()成員函數(shù)進行判空處理。
unique_ptr<int> pInt(new int(8)); unique_ptr<int> pInt1=std::move(pInt); //轉(zhuǎn)移所有權(quán) *pInt=6; //對空智能指針進行賦值操作將報運行時錯誤 if(!pInt.get()) //判空處理更安全 { *pInt=6; }
獨占式的內(nèi)存資源管理可以使用 unique_ptr 來完成,但是如果想對內(nèi)存資源進行共享式管理,那么 unique_ptr 就無能為力了。shared_prt 使用引用計數(shù)來實現(xiàn)對內(nèi)存資源的共享式管理,當(dāng)對內(nèi)存資源的引用計數(shù)變?yōu)?時,由最后一個對內(nèi)存資源擁有管理權(quán)的智能指針對象完成對內(nèi)存資源的釋放。
#include <memory> #include <iostream> using namespace std; class A { public: A() {} ~A() { cout << "A's destructor" << endl; } void Hello() { cout << "use smart pointer to manage memory resources as far as possible" << endl; } }; int main() { shared_ptr<A> spInt(new A); //接管內(nèi)存資源 cout << "reference count "<<spInt.use_count() << endl; shared_ptr<A> spInt1 = spInt; //spInt1獲取內(nèi)存資源的管理權(quán) spInt1->Hello(); cout << "reference count " << spInt.use_count() << endl; spInt1.reset(); //spInt1放棄對內(nèi)存資源的管理權(quán) cout << "reference count " << spInt.use_count() << endl; }
程序編譯運行結(jié)果:
reference count 1
use smart pointer to manage memory resources as far as possible
reference count 2
reference count 1
A's destructor
3.智能指針使用注意事項
智能指針雖然增強了安全性,避免了潛在的內(nèi)存泄漏,但是我們在使用時還是應(yīng)該遵守一定的規(guī)則,以保證代碼的健壯性。
(1)smart_ptr<T> 不等于 T*,使用時不能完全按照T*來使用。因為smart_ptr<T>本質(zhì)上是類對象,一個用于管理內(nèi)存資源的智能指針類對象,而T*是一個指向類型T的指針,二者不能隨意地轉(zhuǎn)換和賦值;
(2)使用獨立的語句將newed對象置入智能指針,因為使用臨時智能指針對象可能會引發(fā)內(nèi)存泄漏。比如下面的語句:
process(shared_ptr<A>(new A),foo());
實際上對 process() 函數(shù)調(diào)用時編譯器需要完成如下三步為process()準備好實參。
(1)調(diào)用函數(shù)foo();
(2)執(zhí)行new A表達式;
(3)調(diào)用shared_ptr<A>構(gòu)造函數(shù),初始化智能指針對象。
實際上,不同的編譯器在執(zhí)行上述三個語句時可能會有不同的順序,如果編譯器將(2.2)放在(2.1)之前執(zhí)行,執(zhí)行順序如下:
(1)執(zhí)行new A表達式;
(2)調(diào)用函數(shù)foo();
(3)調(diào)用shared_ptr<A>構(gòu)造函數(shù),初始化智能指針對象。
如果在調(diào)用函數(shù)foo()時拋出異常,那么new A表達式產(chǎn)生的指向堆對象指針將會丟失,于是產(chǎn)生了內(nèi)存泄漏。解決辦法就是使用獨立的語句將newed對象置入智能指針,做法如下:
shared_ptr<A> spA(new A); process(spA,foo());
以上就是C++如何用智能指針管理內(nèi)存資源的詳細內(nèi)容,更多關(guān)于c++ 智能指針管理內(nèi)存的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++ Invalidaterect()函數(shù)作用案例詳解
這篇文章主要介紹了C++ Invalidaterect()函數(shù)作用案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08VisualStudio Community2019在安裝的過程中無法進入安裝界面的解決方法
這篇文章主要介紹了VisualStudio Community2019在安裝的過程中無法進入安裝界面的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Visual Studio 2019 如何新建 Win32項目的方法步驟
這篇文章主要介紹了Visual Studio 2019 如何新建 Win32項目的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03