智能指針(三) 智能指針的陷阱

一、循環引用問題

shared_ptr出現循環引用的根本原因在於引用計數失效,導致其管理的空間無法釋放。
舉個簡單的例子,class A定義了一個指向自身類類型的智能指針m_spA

class A
{
public:
	A(int i);
	~A() { cout << "A delete!" << endl; }
	shared_ptr<A> m_spA;
private:
	int m_i;
};

A::A(int i) :m_i(i)
{
	cout << "A create!" << endl;
}

假如我們這麼使用class A

int main()
{
	shared_ptr<A> spA=make_shared<A>(1);  //A(1) 引用計數0->1
	spA->m_spA=spA;                       //A(1) 引用計數1->2
}
//spA離開作用域,引用計數2->1!=0,並不會調用A的析構函數

程序的運行結果爲:

A create!

spAspA->m_spA指向的同一塊內存沒有釋放。一個動態資源由多個智能指針管理,一個智能指針可以提供1個引用計數遞減操作,當所有智能指針都完成了遞減操作後資源被釋放。在本例中A資源的兩個智能指針只有一個完成了引用計數遞減,另一個因爲是類內成員所以始終在等待A資源析構後進行遞減操作,而A資源析構也在等待所有智能指針完成遞減操作。

引用計數所在類沒有析構導致引用計數無法正常減少。再看下邊這個例子:

class A
{
public:
	A(int i);
	~A() { cout << "A delete!" << endl; }
	shared_ptr<B> m_spB;
private:
	int m_i;
};

A::A(int i) :m_i(i)
{
	cout << "A create!" << endl;
}

class B
{
public:
	B(int i);
	~B() { cout << "B delete!" << endl; }
	shared_ptr<A> m_spA;
private:
	int m_i;
};

B::B(int i) :m_i(i)
{
	cout << "B create!" << endl;
}

如果這麼調用:

shared_ptr<A> spA=make_shared<A>(1);
shared_ptr<B> spB=make_shared<B>(2);
spA->m_spB=spB;
spB->m_spA=spA;

AB的引用計數都爲2,無論是內部的智能指針遞減操作都在等待析構函數的調用,而各自的析構函數又在等待指針的遞減,出現循環引用。weak_ptr的出現就是爲了解決循環引用的問題,weak_ptr是一個觀察者,其特點是不會增加管理對象的引用計數,但是可以lock方法對獲取一個對應的智能指針,從而完成對動態對象的操作。

二、混合使用智能和普通指針

普通指針不要和智能指針混合使用,當我們講一個普通指針綁定到智能指針時,意味着我們將普通指針管理的資源交給了智能指針。下面是混合使用智能指針和普通指針的例子:

double * pd=new double(1);
shared_ptr<double> spd1(pd);
shared_ptr<double> spd2(pd);

因爲spd1spd2智能指針無法獲知當前內存實際的引用計數,導致了指向同一內存的智能指針狀態的獨立,spd1spd2在離開作用域時都將會釋放一次對應內存,造成“同一內存多次釋放”的問題。

再看另一個混合使用智能和普通的例子:

void use_smartPtr(shared_ptr<int> sp)
{
	*sp=4396;
}

內置指針 int * p=new int(3)不能傳入use_smartPtr是因爲接收內置指針的構造函數是explict的,只能通過以下方法構造一個臨時的shared_ptr:

use_smartPtr(shared_ptr<int>(p));

p指針初始化了一個臨時共享指針,調用結束之後臨時變量被銷燬,對應內存被回收,任何嘗試使用p的操作都是未定義的,多次調用use_smartPtr甚至會出現多次釋放的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章