在上一篇中介绍了auto_ptr的实现,我们说那是一种不太好的实现方法,其实主要就是在解决拷贝构造所带来的问题,
既然这样的话,有人就有了一种方法,既然拷贝会出现各种问题,那么我禁止拷贝!
scoped_ptr的实现就是一种防拷贝的机制。scoped(作用域的意思)
1.只声明不定义
2.声明为私有的,让在类外也没有办法定义
下面是模拟库里的scoped_ptr的实现如下代码:
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
template<class T>
class scoped_ptr
{
public:
scoped_ptr(T * ptr):_ptr(ptr)
{}
~scoped_ptr()
{
delete _ptr;
}
T operator*()
{
return *_ptr;
}
T *operator->()
{
return _ptr;
}
private:
T *_ptr;
scoped_ptr(const scoped_ptr<T> & p1);//采用防拷贝
scoped_ptr<T> &operator =(const scoped_ptr<T> & p1);
};
void test()
{
scoped_ptr<string> p1(new string("hello"));
cout<<*p1<<endl;
scoped_ptr<string> p2(new string("world"));
p2=p1;
scoped_ptr<string> p3(p1);
}
int main()
{
test();
return 0;
}
ScopedPtr也是一种实现起来比较简单的方法
那么处理拷贝构造的问题和赋值运算符重载的问题,在之前我们说可以采用一种引用计数的方法,share_ptr就是这样实现的
下面来模拟库里面的share_ptr实现代码如下:
#include <stdio.h>
#include <string>
using namespace std;
template<class T>
class SharePtr
{
public:
SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
{
}
~SharePtr()
{
if(--(*_pCount)==0)
{
delete _ptr;
delete _pCount;
}
}
SharePtr(const SharePtr<T> &p1)
{
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
SharePtr<T> &operator=(const SharePtr<T> & p1)
{
if(_ptr!=p1._ptr)
{
if(--(*_pCount)==0)
{
delete _ptr;
delete _pCount;
}
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
return * this;
}
T operator*()
{
return *_ptr;
}
T * operator->()
{
return _ptr;
}
private:
T *_ptr;
int *_pCount;
};
void test()
{
SharePtr<string> p1(new string("hello"));
SharePtr<string> p2(new string("world"));
cout<<*p1<<endl;
cout<<*p2<<endl;
p2=p1;
cout<<*p2<<endl;
SharePtr<string> p3(p1);
cout<<*p3<<endl;
}
int main()
{
test();
return 0;
}
这里的实现与之前实现的string 很相似。
但是这里又会有新的问题出现
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
template<class T>
class SharePtr
{
public:
SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
{
}
~SharePtr()
{
if(--(*_pCount)==0)
{
delete _ptr;
delete _pCount;
cout<<"~SharePtr()"<<endl;
}
}
SharePtr(const SharePtr<T> &p1)
{
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
SharePtr<T> &operator=(const SharePtr<T> & p1)
{
if(_ptr!=p1._ptr)
{
if(--(*_pCount)==0)
{
delete _ptr;
delete _pCount;
}
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
return * this;
}
T operator*()
{
return *_ptr;
}
T * operator->()
{
return _ptr;
}
int GetCount()
{
return *_pCount;
}
private:
T *_ptr;
int *_pCount;
};
struct ListNode
{
SharePtr<ListNode> _next;
SharePtr<ListNode> _prev;
int _data;
ListNode(int val=0):_next(NULL),_prev(NULL),_data(val)
{}
};
void test()
{
// ListNode *node1=new ListNode(1);
// ListNode *node2=new ListNode(2);
// //因为中间可能出现异常
// //所以需要用智能指针来管理
// delete node1;
// delete node2;
SharePtr<ListNode> node1(new ListNode(1));
SharePtr<ListNode> node2(new ListNode(2));
cout<<"node1.count:"<<node1.GetCount()<<endl;
cout<<"node2.count:"<<node2.GetCount()<<endl;
node1->_next=node2;
node2->_next=node1;
cout<<"node1.count:"<<node1.GetCount()<<endl;
cout<<"node2.count:"<<node2.GetCount()<<endl;
}
int main()
{
test();
return 0;
}
我们看到,当节点里面的next和prev的指向改变时,会改变node1和node2的引用计数,即使程序结束,也最终只是将引用计数减一,并没有释放节点,造成这种问题,我们称为循环引用。
造成这种问题的原因是,在修改next和prev指向时 不能修改引用计数
这里提出了一个辅助share_ptr的weak_ptr,(weak_ptr接收的是share_ptr,只是处理时并不处理引用计数)
模拟实现如下:
#include <stdio.h>
#include <string>
using namespace std;
template<class T>
class SharePtr;
template<class T>
class WeakPtr
{
public:
WeakPtr(const SharePtr<T> p1):_ptr(p1._ptr)
{}
T &operator*()
{
return * _ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};
template<class T>
class SharePtr
{
friend class WeakPtr<T>;//因为WeakPtr中要用到其私有成员,将WeakPtr声明为其友元类
public:
SharePtr(T * ptr):_ptr(ptr),_pCount(new int(1))
{
}
~SharePtr()
{
if(--(*_pCount)==0)
{
if(_ptr)
{
delete _ptr;
cout<<"~SharePtr()"<<endl;
}
delete _pCount;
}
}
SharePtr(const SharePtr<T> &p1)
{
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
SharePtr<T> &operator=(const SharePtr<T> & p1)
{
if(_ptr!=p1._ptr)
{
if(--(*_pCount)==0)
{
delete _ptr;
delete _pCount;
}
_ptr=p1._ptr;
_pCount=p1._pCount;
++*_pCount;
}
return * this;
}
T operator*()
{
return *_ptr;
}
T * operator->()
{
return _ptr;
}
int GetCount()
{
return *_pCount;
}
private:
T *_ptr;
int *_pCount;
};
struct ListNode
{
WeakPtr<ListNode> _next;
WeakPtr<ListNode> _prev;
int _data;
ListNode(int val=0):_next(NULL),_prev(NULL),_data(val)
{}
};
void test()
{
// ListNode *node1=new ListNode(1);
// ListNode *node2=new ListNode(2);
// //因为中间可能出现异常
// //所以需要用智能指针来管理
// delete node1;
// delete node2;
SharePtr<ListNode> node1(new ListNode(1));
SharePtr<ListNode> node2(new ListNode(2));
node1->_next=node2;
node2->_next=node1;
cout<<"node1.count:"<<node1.GetCount()<<endl;
cout<<"node2.count:"<<node2.GetCount()<<endl;
}
int main()
{
test();
return 0;
}
如上,就可以解决循环引用的问题。
注意:我们这里的WeakPtr是里面没有_pCount成员(引用计数),但是库里面是有的,库里面的share_ptr是借助了6个类来实现的。weak_ptr中的引用计数和share_ptr中的引用计数是一致的,share_ptr中的引用计数加加时,weak_ptr中的引用计数也要进行加加。它的引用计数是为了看自己的指针是否有效,我了解的也不是很多,大家可以自己下去研究。