c++ 11學習筆記--智能指針
C++ 98的 std::auto_ptr已經被徹底遺棄了,取而代之的是unique_ptr、shared_ptr與weak_ptr。大部分時候我們自己手動申請內存方式內存都是沒有問題的,問題是如果程序很大了之後,一個復雜的對象,多次拷貝的代價非常高,很多地方都會使用到,只存在一份拷貝顯然是最好的,這個時候對象生命周期的管理就會很復雜,所以c++引入了智能指針。
任何事物都會有兩面性。
Shared_ptr
摘錄於Effective C++, 3rd Edition, Item 17: 在 standalone statements(獨立語句)中將 new 出來的 objects(對象)存入 smart pointers(智能指針)
比如
復制代碼
class Widget {
public:
Widget() {
cout << "construct Widget!!!" << endl;
}
;
~Widget() {
cout << "destruct Widget!!!" << endl;
}
;
};
int priority()
{
cout << "22222" << endl;
return 0;
}
void processWidget(int priority,std::shared_ptr<Widget> pw)
{
cout << "111111" << endl;
}
int main()
{
processWidget(priority(),std::shared_ptr<Widget>(new Widget));
return 0;
}
復制代碼
processWidget運行的過程應該是
1. New Widget
2. shared_ptr constructor
3. Priority()
我在llvm上測試的結果也是這個執行順序
也許某些編譯器上可能執行的順序是
New Widget
Priority() //發生異常
shared_ptr constructor
就有可能有內存洩露
所以最好的辦法還是應該把new widget提到外面來寫,所以好的編碼習慣很重要。
2.循環引用
復制代碼
#include <iostream>
using namespace std;
class A;
class B;
typedef std::shared_ptr<A> APtr;
typedef std::shared_ptr<B> BPtr;
class A {
public:
BPtr b;
~A () {
cout << "A released" << endl;
}
};
class B {
public:
APtr a;
~B () {
cout << "B released" << endl;
}
};
int main () {
APtr a(new A());
BPtr b(new B());
a->b = b; // 1
b->a = a; // 2
cout << "over!!" << endl;
return 0;
}
復制代碼
要解決這個問題就需要引入一個弱引用的智能指針了weak_ptr
復制代碼
class A;
class B;
typedef std::shared_ptr<A> APtr;
typedef std::shared_ptr<B> BPtr;
typedef std::weak_ptr<A> AWeakPtr;
typedef std::weak_ptr<B> BWeakPtr;
class A {
public:
BWeakPtr b; // 注意這裡
~A () {
cout << "A released" << endl;
}
};
class B {
public:
AWeakPtr a; // 注意這裡
~B () {
cout << "B released" << endl;
}
};
int main () {
APtr a(new A());
BPtr b(new B());
a->b = b;
b->a = a;
return 0;
}
復制代碼
這兩種指針其實和oc裡面的 strong, weak非常相識。
weak_ptr的另外一種用法
使用情景:當類對象被 shared_ptr 管理時,需要在類自己定義的函數裡把當前類對象作為參數傳給其他函數時,這時需要傳遞一個 shared_ptr ,否則就不能保持 shared_ptr 管理這個類對象的語義(因為有一個 raw pointer 指向這個類對象,而 shared_ptr 對類對象的這個引用沒有計數,很有可能 shared_ptr 已經把類對象資源釋放了,而那個調用函數還在使用類對象——顯然,這肯定會產生錯誤)。《摘錄:http://blog.csdn.net/zhongguoren666/article/details/8617436》
直接看官網的例子吧:
《http://en.cppreference.com/w/cpp/memory/enable_shared_from_this》
復制代碼
#include <memory>
#include <iostream>
struct Good: std::enable_shared_from_this<Good>
{
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
struct Bad
{
std::shared_ptr<Bad> getptr() {
return std::shared_ptr<Bad>(this);
}
~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
class CObj: public std::enable_shared_from_this<CObj> {
friend class CObjMgr;
protected:
CObj() {} // 只有CObjMgr可以創建與刪除
~CObj(){}
};
int main()
{
// Good: the two shared_ptr's share the same object
std::shared_ptr<Good> gp1(new Good);
std::shared_ptr<Good> gp2 = gp1->getptr();
std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
// Bad, each shared_ptr thinks it's the only owner of the object
std::shared_ptr<Bad> bp1(new Bad);
std::shared_ptr<Bad> bp2 = bp1->getptr();
std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
} // UB: double-delete of Bad
復制代碼
1.絕對不能在構造函數中調用shared_from_this()
因為shared_ptr裡面初始化enable_shared_from_this的成員weak_ptr, 這個時候weak_ptr還是空值。
2.為什麼內部不能用this指針
因為我們程序中用shared_ptr來管理指針,如果我們在類的內部傳遞的過程中用原始指針,這樣類內部的引用shared_ptr不會察覺到,因為有可能我們傳進去的時候已經被shared_ptr釋放掉了。
unique_ptr
相對就要單純許多了,unique_ptr“唯一”擁有其所指對象,只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現)。
代替普通指針
復制代碼
void foo()
{//不安全的代碼
X *px = new X;
// do something, exception may occurs
delete px; // may not go here
}
unique_ptr<X> px(new X);
復制代碼
在函數中返回對象
unique_ptr<X> foo()
{
unique_ptr<X> px(new X);
// do something
return px; //移動語義
}
放入容器中
vector<unique_ptr<string>> vs { new string{“1111”}, new string{“2222”},new string{“3333”} };
vector<unique_ptr<string>>v;
unique_ptr<string> test(new string("11111"));
v.push_back(std::move(test));//使用移動語法
支持直接持有數組
std::shared_ptr<int> p(new int[10],
[](int* p){
delete[] p;
});
//或者使用helper
std::shared_ptr<int> p(new int[10],std::default_delete<int[]>());