你說你會C++? —— 智能指針
智能指針能夠解決上面遇到的問題。
C++中常見的智能指針包含(共七種): std::auto_ptr boost::scoped_ptr boost::shared_ptr boost::intrusive_ptr boost::scoped_array boost::shared_array boost::weak_ptr
兩個方法:
- get()
訪問智能指針包括的裸指針引用
- reset()
若傳遞參數為空或NULL 則智能指針會釋放當前管理的內存。
若傳遞參數為一個對象 則智能指針會釋放當前管理的內存,管理新傳入的對象。
假定有以下這個ManagedObj類。
class ManagedObj { public: ManagedObj(int val = 0):m_val(val) { cout<<"Obj : "<<m_val<<endl; } ~ManagedObj() { cout<<"~Obj : "<<m_val<<endl; } void testFun() { cout<<"testFun : "<<m_info<<endl; } public: string m_info; int m_val; };
-> std::auto_ptr
屬於STL 在namespace std中 加入頭文件 #include <memory>就可以使用。 auto_ptr一般用於管理單個堆內存對象。
看以下這個樣例:
// std::auto_ptr void testAutoPtr1() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { atPtr->testFun(); atPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 atPtr->testFun(); (*atPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*atPtr).testFun(); } }
OK 好像沒什麽問題。
我們接著測試:
void testAutoPtr2() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { auto_ptr<ManagedObj> atPtr2; atPtr2 = atPtr; // 原因在這行代碼 atPtr2->testFun(); atPtr->testFun(); // 崩潰在這行代碼 } }執行結果為:
調試發現 最後一行代碼出bug了。 為什麽呢? 事實上是atPtr2剝奪了atPtr的內存管理全部權,導致atPtr懸空。
繼續測試。
void testAutoPtr3() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { atPtr.release(); } }執行結果為:
好像又出bug了。 我們並沒有看到析構函數被調用的跡象,內存泄漏了。。 (逗我呢 怎麽這麽多bug?
)
別著急 第三個測試函數正確的寫法應該是這種。
void testAutoPtr3() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { //ManagedObj* temp = atPtr.release(); //delete temp; // 或者 atPtr.reset(); } }
這才是我們想要看到的結果。
所以我們也發現。auto_ptr的release()函數僅僅是讓出內存的管理權,並沒有真正的釋放內存。
總結:
auto_ptr一般用於管理單個堆內存對象。
可是須要註意: a. 切記使用 "operator="。萬一真用了,就不要再使用舊的對象了。
b. release()方法不會釋放內存 只不過讓出全部權。
上面的auto_ptr使用起來確實不是非常方便,並且我們還有註意避開它的使用缺陷。
因此就出現了boost中的一些智能指針,以下一一介紹。
-> boost::scoped_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
關於boost庫的安裝教程 可參考:
http://blog.csdn.net/misskissc/article/details/9793645
http://www.cnblogs.com/xuqiang/archive/2011/05/08/2040420.html
與auto_ptr類似。scoped_ptr也可管理單個堆內存的對象,
可是它獨享全部權,因此也就避免了auto_ptr的缺陷。
OK。上代碼測試。
// boost::scoped_ptr void testScopedPtr() { boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize")); if (scPtr.get()) { scPtr->testFun(); scPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 scPtr->testFun(); (*scPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*scPtr).testFun(); //scPtr.release(); // error scoped_ptr 沒有成員release //boost::scoped_ptr<ManagedObj> scPtr2; //scPtr2 = scPtr; // error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪問 } }執行結果為:
註意凝視部分的代碼。 scoped_ptr沒有release()函數 屏蔽了operator=操作, 因此不會出現auto_ptr中出現的內存泄漏和崩潰問題。 但這也帶來了還有一個新問題。我們無法復制智能指針。
因此出現了接下來的shared_ptr。
-> boost::shared_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
因為scoped_ptr不同意賦值 拷貝 獨享內存的全部權。
這裏的shared_ptr是專門解決智能指針的內存全部權共享這個問題的。
其內部使用了引用計數。
// boost::shared_ptr void testSharedPtr(boost::shared_ptr<ManagedObj> ptr) { ptr->testFun(); cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl; } void testSharedPtr1() { boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize")); if (shPtr.get()) { shPtr->testFun(); shPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 shPtr->testFun(); (*shPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*shPtr).testFun(); } cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl; testSharedPtr(shPtr); cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl; //shPtr.release(); // error shared_ptr 沒有成員release }執行結果為:
能夠看到。調用testSharedPtr()函數運行了一次復制操作,智能指針內部的引用計數+1。 在該函數調用結束後,引用計數自己主動-1。 上面介紹的幾種智能指針都針對單個對象的內存管理。
事實上智能指針也可管理數組,接下來介紹boost::scoped_array和boost::shared_array。
-> boost::scoped_array
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
boost::scoped_array用於管理動態數組。也是獨享全部權的。
上代碼:
// boost::scoped_array void testScopedArray() { boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]); // 動態數組初始化 if (scArr.get()) { scArr[0].testFun(); scArr.get()[0].m_info += " [0] 1st append"; scArr[0].testFun(); //(*scArr)[0].m_info += " 2st append"; // error scoped_array沒有重載operator* //(*scArr)[0].testFun(); //scArr[0].release(); // error scoped_array 沒有成員release boost::scoped_array<ManagedObj> scArr2; //scArr2 = scArr; // error operator=不可訪問 屏蔽了operator= 禁止拷貝 } }執行結果為:
boost::scoped_array的使用和boost::scoped_ptr大致幾乎相同。 都禁止拷貝 獨享全部權。
註意:
boost::scoped_array沒有重載operator*操作符
-> boost::shared_array
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
內部使用了引用計數,解決參數傳遞和拷貝賦值時的問題。
以下給出代碼測試:
// boost::shared_array void testSharedArray(boost::shared_array<ManagedObj> pArr) { cout<<"shared_arr use_count: "<<pArr.use_count()<<endl; boost::shared_array<ManagedObj> pTempArr; pTempArr = pArr; cout<<"shared_arr use_count: "<<pArr.use_count()<<endl; } void testSharedArray1() { boost::shared_array<ManagedObj> shArr(new ManagedObj[2]); if (shArr.get()) { shArr[0].testFun(); shArr.get()[0].m_info += " [0] 1st append"; shArr[0].testFun(); shArr[1].testFun(); shArr.get()[0].m_info += " [1] 1st append"; shArr[1].testFun(); //(*shArr)[0].m_info += " [0] 2nd append"; // error 沒有重載operator*操作符 } cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl; testSharedArray(shArr); cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl; }執行結果為:
-> boost::weak_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
若我們只關心是否能使用對象。而不關心內部的引用計數。 boost::weak_ptr 是 boost::shared_ptr 的觀察者(Observer)對象, 意味著boost::weak_ptr僅僅對boost::shared_ptr引用。可是並不更新 其引用計數。被觀察的shared_ptr失效後,weak_ptr對應也失效。
上測試代碼:
// boost::weak_ptr void testWeakPtr() { boost::weak_ptr<ManagedObj> wPtr; boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize")); cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl; wPtr = shPtr; cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl; }執行結果為:
我們發現賦值操作並沒有更改引用計數。 boost::weak_ptr很多其它用於以下這樣的場合: 在基類中定義一個weak_ptr,用於觀察其子類中的shared_ptr。這樣基類僅僅要看自己的weak_ptr 是否為空就知道子類有沒對自己賦值,而不影響子類中shared_ptr的引用計數。從而減少復雜度。更好管理對象。
-> boost::intrusive_ptr
屬於boost庫 在namespace boost中 加入頭文件 #include<boost/smart_ptr.hpp> 就可以使用。
intrusive_ptr是一種插入式智能指針,其內部沒有引用計數, 須要自己增加引用計數,否則會編譯錯誤。
OK 到這兒為止,七種智能指針基本上都大致介紹完了。
參考:
http://blog.csdn.net/xt_xiaotian/article/details/5714477
完整的代碼在這裏。
#include <iostream> #include <memory> #include <string> #include <boost/smart_ptr.hpp> // add header file // namespace using namespace std; using namespace boost; // heap obj class class ManagedObj { public: ManagedObj(int val = 0, string info = "") :m_val(val), m_info(info) { cout<<"Obj : "<<m_val<<m_info<<endl; } ~ManagedObj() { cout<<"~Obj : "<<m_val<<m_info<<endl; } void testFun() { cout<<"testFun : "<<m_info<<endl; } public: string m_info; int m_val; }; // std::auto_ptr void testAutoPtr1() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { atPtr->testFun(); atPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 atPtr->testFun(); (*atPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*atPtr).testFun(); } } void testAutoPtr2() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { auto_ptr<ManagedObj> atPtr2; atPtr2 = atPtr; atPtr2->testFun(); atPtr->testFun(); } } void testAutoPtr3() { auto_ptr<ManagedObj> atPtr(new ManagedObj(1, " initialize")); if (atPtr.get()) // 推斷智能指針是否為空 { //ManagedObj* temp = atPtr.release(); //delete temp; // 或者 atPtr.reset(); } } // boost::scoped_ptr void testScopedPtr() { boost::scoped_ptr<ManagedObj> scPtr(new ManagedObj(1, " initialize")); if (scPtr.get()) { scPtr->testFun(); scPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 scPtr->testFun(); (*scPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*scPtr).testFun(); //scPtr.release(); // error scoped_ptr 沒有成員release //boost::scoped_ptr<ManagedObj> scPtr2; //scPtr2 = scPtr; // error scoped_ptr<T>::operator=(const scoped_ptr<ManagedObj> &)不可訪問 } } // boost::shared_ptr void testSharedPtr(boost::shared_ptr<ManagedObj> ptr) { ptr->testFun(); cout<<"shared_ptr use_count: "<<ptr.use_count()<<endl; } void testSharedPtr1() { boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, " initialize")); if (shPtr.get()) { shPtr->testFun(); shPtr.get()->m_info += " 1st append"; // get() 返回裸指針的引用 shPtr->testFun(); (*shPtr).m_info += " 2nd append"; // operator* 返回智能指針管理的對象 (*shPtr).testFun(); } cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl; testSharedPtr(shPtr); cout<<"shared_ptr1 use_count: "<<shPtr.use_count()<<endl; //shPtr.release(); // error shared_ptr 沒有成員release } // boost::scoped_array void testScopedArray() { boost::scoped_array<ManagedObj> scArr(new ManagedObj[2]); // 動態數組初始化 if (scArr.get()) { scArr[0].testFun(); scArr.get()[0].m_info += " [0] 1st append"; scArr[0].testFun(); //(*scArr)[0].m_info += " 2st append"; // error scoped_array沒有重載operator* //(*scArr)[0].testFun(); //scArr[0].release(); // error scoped_array 沒有成員release boost::scoped_array<ManagedObj> scArr2; //scArr2 = scArr; // error operator=不可訪問 屏蔽了operator= 禁止拷貝 } } // boost::shared_array void testSharedArray(boost::shared_array<ManagedObj> pArr) { cout<<"shared_arr use_count: "<<pArr.use_count()<<endl; boost::shared_array<ManagedObj> pTempArr; pTempArr = pArr; cout<<"shared_arr use_count: "<<pArr.use_count()<<endl; } void testSharedArray1() { boost::shared_array<ManagedObj> shArr(new ManagedObj[2]); if (shArr.get()) { shArr[0].testFun(); shArr.get()[0].m_info += " [0] 1st append"; shArr[0].testFun(); shArr[1].testFun(); shArr.get()[1].m_info += " [1] 1st append"; shArr[1].testFun(); //(*shArr)[0].m_info += " [0] 2nd append"; // error 沒有重載operator*操作符 } cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl; testSharedArray(shArr); cout<<"shared_arr1 use_count: "<<shArr.use_count()<<endl; } // boost::weak_ptr void testWeakPtr() { boost::weak_ptr<ManagedObj> wPtr; boost::shared_ptr<ManagedObj> shPtr(new ManagedObj(1, "initialize")); cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl; wPtr = shPtr; cout << "testWeakPtr boost::shared_ptr UseCount: " << shPtr.use_count() << endl; } // boost::intrusive_ptr void testIntrusivePtr() { //boost::intrusive_ptr<ManagedObj> intPtr(new ManagedObj[1], false); // error 要自己加入引用計數 } // main function int main(void) { // -----std::auto_ptr----- //testAutoPtr(); //testAutoPtr2(); //testAutoPtr3(); // -----boost::scoped_ptr----- //testScopedPtr(); // -----boost::shared_ptr----- //testSharedPtr1(); // -----boost::scoped_array----- //testScopedArray(); // -----boost::shared_array----- //testSharedArray1(); // -----boost::weak_ptr----- testWeakPtr(); return 0; }
你說你會C++? —— 智能指針