C++ 標準庫智能指針
整理一下c++中shared_ptr,weak_ptr,unique_ptr三種指針的使用案例和註意事項,讓程序資源更加案例,在標準庫中,需要包含<memory>,在boost庫中,
一. 智能指針unique_ptr
與shared_ptr相似,區別在於unique_ptr是獨立擁有對象權,因此只有move語言,無拷貝語義,不做其它詳述了。
二.智能指針share_ptr
1.基本使用
class Sam { public: Sam(int v):val(v) { } int32_t val;
~Sam()
std::cout << "~sam()" << std::endl;
}
};
//define std::shared_ptr<Sam> p1(new Sam(100)); std::shared_ptr<Sam> p2 = std::make_shared<Sam>(200);
p1.reset(new Sam(300)); //new first, delete second
std::shared_ptr<Sam> p3 = p1;
//get the base ptr
Sam* pSam = p1.get();
//modify
(*p1).val = 10000;
//cal is 1
bool b = p1.unique();
p1.reset();
2.高級使用
(1)使用shared_ptr<list<T>>類型,調用reset的時候list中的所有的Sam對象都會調用析構函數
std::shared_ptr<std::list<Sam>> lst_ptr( new std::list<Sam>(8, 33)); std::cout << lst_ptr.get()->size() << std::endl; lst_ptr.reset();
(2)程序不知道自己需要使用多少對象. 且程序需要在多個對象間共享數據,使用vector<shared_ptr<T>>類型:
std::vector<std::shared_ptr<Sam>> vec_ptr; std::shared_ptr<Sam> p1(new Sam(10)); vec_ptr.push_back(p1);
(3)定制自己的刪除器:在shared_ptr釋放時會自動調用 函數刪除器而不是默認的析構函數了:
void Deleter(Sam* obj) { std::cout << "Deleter" << std::endl; }
std::shared_ptr<Sam> sp(new Sam(10), Deleter);
如果將刪除器定義成類,則自由性更大,下面的代碼執行後會調用析構函數(簡單地使用了delete)
template<typename T> class Deleter { public: void operator () (T* x) const { if (x != NULL) { std::cout << __LINE__ << std::endl; delete x; x = NULL; } } };
std::shared_ptr<Sam> sp(new Sam(10), Deleter<Sam> {})
3.錯誤用法
情形一:一個指針同時放入兩個shared_ptr,會在第二個shared_ptr釋放時引發異常。
pSam = new Sam(400); std::shared_ptr<Sam> p4(pSam); std::shared_ptr<Sam> p5(pSam);
情形二:數據結構形成環的時候,shared_ptr不能正常工作,需要與weak_ptr協作解決此問題,用例如下:
class CB; class CA; class CA { public: CA() {} ~CA() { std::cout << "~CA()" << std::endl; } void Register(const std::shared_ptr<CB>& sp) { m_sp = sp; } private: std::shared_ptr<CB> m_sp; }; class CB { public: CB() {}; ~CB() { std::cout << "~CB()" << std::endl; }; void Register(const std::shared_ptr<CA>& sp) { m_sp = sp; } private: std::shared_ptr<CA> m_sp; }; std::shared_ptr<CA> spa(new CA); std::shared_ptr<CB> spb(new CB); spb->Register(spa); spa->Register(spb); printf("%d\n", spb.use_count()); // 2 printf("%d\n", spa.use_count()); // 2
程序結束後,無法釋放內存,也沒有調用析構函數,智能指針的引用計數都是2,這就是循環引用問題。
情形三:普通類繼承 enable_shared_from_this 的錯誤情況:
class Y : public std::enable_shared_from_this<Y> { public: std::shared_ptr<Y> GetSharePtr() { return shared_from_this(); } };
Y y;
std::shared_ptr<Y> spy = y.GetSharePtr(); // 錯誤, y 根本不是 new 創建的
Y* y = new Y;
std::shared_ptr<Y> spy = y->GetSharePtr(); // 錯誤, 問題依舊存在, 程序直接崩潰
std::shared_ptr<Y> spy(new Y);
std::shared_ptr<Y> p = spy->GetSharePtr();
printf("%d\n", p.use_count()); // 2
前兩者錯誤是因為雖然Y由 enable_shared_from_this派生,但智能指針的數據結構並沒有因為new Y的操作賦值 。
三. 智能指針weak_ptr
構造和析構不會引起引用計數的增加或減少。沒有重載 * 和 -> 但可以使用lock獲得一個可用的shared_ptr對象,且在所指對象內存已經無效時,返回指針空值nullptr.
帶有的成員函數reset,use_count
std::shared_ptr<Sam> sam_ptr(new Sam(6)); std::weak_ptr<Sam> sam_wk = sam_ptr; std::shared_ptr<Sam> sp = sam_wk.lock(); if (sp) { std::cout << (*sp).val << endl; // 6 } std::cout << sp.use_count() << std::endl; //2
C++ 標準庫智能指針