模擬實現智慧指標
眾所周知,智慧指標是用來管理指標的,為了避免開闢了空間而忘記釋放的情況。
下面我們分別來模擬實現以下boost庫中三種智慧指標的實現,他們分別是auto ptr,scoped ptr,shared ptr。
一.auto ptr
- 舊版本的實現:
template <typename T>
class Auto_ptr
{
public:
Auto_ptr(T*ptr = NULL) :_ptr(ptr), state(true)
{}
~Auto_ptr()
{
if (state&&NULL != _ptr)
{
delete _ptr;
}
}
Auto_ptr(const Auto_ptr<T>&sp)
{
_ptr = sp._ptr;
sp.state = false;//賦值以後將狀態置為false;
}
Auto_ptr<T>&operator=(const Auto_ptr<T>&sp)
{
if (this != &sp)
{
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
_ptr = sp._ptr;
sp.state = false;//賦值以後將狀態置為false;
}
return *this;
}
private:
T*_ptr;
bool state;
};
2.新版本的實現:
賦值以後將原指標置為空,而不是改變狀態
3.總結
auto ptr只適用於單個的指標的情況,當進行物件之間的賦值的時候會使所有權轉移。所以建議在任何情況下都不使用auto ptr。
二.scoped ptr
scoped ptr是對auto ptr的改進,將拷貝構造以及賦值操作符的過載設為私有。適用於開闢一個型別空間的指標以及多個型別空間的指標。
三.shared ptr
template<typename T>
class Shared_ptr
{
public:
Shared_ptr(T*ptr = NULL) :_ptr(ptr), pcount(NULL)
{
if (NULL != _ptr)
{
pcount = new int(1);
}
}
~Shared_ptr()
{
if (_ptr&&--(*pcount) == 0)
{
delete _ptr;
delete pcount;
}
}
Shared_ptr(const Shared_ptr<T>&sp)
{
_ptr = sp._ptr;
pcount = sp.pcount;
++(*pcount);
}
Shared_ptr<T>&operator=(const Shared_ptr<T>&sp)
{
if (this != &sp)
{
if (_ptr&&--(*pcount)==0)
{
delete pcount;
delete _ptr;
}
pcount = sp.pcount;
_ptr = sp._ptr;
++(*pcount);
}
return *this;
}
int use_count()
{
return *pcount;
}
private:
T*_ptr;
int*pcount;
};
使用引用計數進行實現,適用於開闢多個空間的指標和一個空間的指標。可以進行指標間的賦值。但是它同時也帶來了一些問題。
1. 引用計數更新存在著執行緒安全(這裡暫且不提)
2. 迴圈引用
3. 定置刪除器
這裡重點講一下迴圈引用的問題:
struct ListNode
{
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
/*weak_ptr<ListNode > _prev;
weak_ptr<ListNode > _next;*/
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void Test()
{
// 迴圈引用問題
shared_ptr <ListNode > p1(new ListNode());
shared_ptr <ListNode > p2(new ListNode());
cout << "p1->Count:" << p1.use_count() << endl;//1
cout << "p2->Count:" << p2.use_count() << endl;//1
// p1節點的_next指向 p2節點
p1->_next = p2;
// p2節點的_prev指向 p1節點
p2->_prev = p1;
cout << "p1->Count:" << p1.use_count() << endl;//2
cout << "p2->Count:" << p2.use_count() << endl;//2
}
由程式碼可知p1本就維護一塊空間,現將p1賦值給p2的_next也就兩個指標在維護一塊空間,p1的引用計數為2,p2的引用計數也為2。
(1)當p2出了作用域,引用計數變為1,不為0,所以沒有釋放空間。
(2)當p1出了作用域,引用計數變為1,不為0,所以沒有釋放空間。
畫個圖來解釋一下(1)(2):
p2出作用域,它本身要呼叫解構函式,所以引用計數減1,但此時p1的_next的值是p2,也就是說p2同時被p1管理著,p1的空間沒有釋放,所以p2的引用計數不會變為0。
同理,因為p2沒有釋放,p1也無法釋放。
解決:將ListNode中_prev和_next的型別設為weak ptr型別。
原因:weak ptr和shared ptr都是引用計數基類的派生類並且weak ptr維護的引用計數和shared ptr的不一樣。