众所周知,智能指针是用来管理指针的,为了避免开辟了空间而忘记释放的情况。
下面我们分别来模拟实现以下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的不一样。