上一篇博客我们简单介绍了智能指针:简单介绍智能指针
有关shared_ptr的循环引用问题可以参考博客:weak_ptr–解决shared_ptr循环引用问题
auto_ptr
模拟实现
template<class T>
class Autoptr
{
public:
Autoptr(T* ptr = NULL)
:_ptr(ptr)
{}
Autoptr(Autoptr<T>& ap)
{
_ptr = ap._ptr;
ap._ptr = NULL;
}
Autoptr<T>& operator=(Autoptr<T>& ap)
{
if (this != &ap)
{
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
T* operator->()
{
return _ptr;
}
T operator*()
{
return *_ptr;
}
~Autoptr()
{
delete _ptr;
_ptr = NULL;
}
void Reset(T* ptr = 0)
{
if (_ptr != ptr)
{
delete _ptr;
}
_ptr = ptr;
}
protected:
T* _ptr;
};
void APTest()
{
Autoptr<int> ap1(new int(10));
cout <<"*ap1 = "<< *ap1 << endl;
Autoptr<int> ap2(ap1);
cout <<"*ap2 = "<< *ap2 << endl;
Autoptr<int> ap3(new int(20));
ap3 = ap2;
cout <<"*ap3 = "<< *ap3 << endl;
//cout << *ap1 << endl;//报错
}
测试结果:
代码分析
代码中的Reset()函数是干嘛的?
void Reset(T* ptr = 0)
{
if (_ptr != ptr)
{
delete _ptr;
}
_ptr = ptr;
}
答:为了重置一个auto_ptr对象,必须使用reset()函数。可以向reset()传递一个指针,如果不希望设置该auto_ptr对象的话,可以传递一个0值。如果auto_ptr当前指向一个对象并且该auto_ptr对象拥有该对象的所有权,则该对象再底层指针重置之前,首先被删除。例如:
void APTest()
{
int *pi = new int(20);
auto_ptr<int>_auto_ptr(pi);
cout << "_auto_ptr = " << *pi << endl;
int *pi2 = new int(30);
_auto_ptr.reset(pi2);
cout << "pi->" << *pi << endl;
cout << "pi2->" << *pi2 << endl;
}
输出结果:
我们可以在输出结果的第二行看到,pi已经变成了随机值。reset()函数将auto_ptr对象原来指向的那块动态分配的空间释放了。
上面的代码还有一句:
//cout << *ap1 << endl;//报错
为什么报错??
这是因为ap1已经把它指向的空间交给了ap2去管理,所以ap1已经不具备访问原来自己所指向的空间的权限。所以对它进行解引用是非法的。
所以可以看清auto_ptr的本质是管理权的转移,即ap1将自己所指向的空间交给ap2来管理,析构也是由ap2来完成。
scoped_ptr
由于auto_ptr的严重缺陷,所以后来引入了scoped_ptr。防拷贝,意思就是不能进行拷贝,简单地说是一种简单粗暴的方式。
模拟实现
template<class T>
class Scopedptr
{
public:
Scopedptr(T* ptr = NULL)
:_ptr(ptr)
{}
T* operator->()
{
return _ptr;
}
T operator*()
{
return *_ptr;
}
~Scopedptr()
{
delete _ptr;
_ptr = NULL;
}
protected:
Scopedptr(Scopedptr<T>& sp);
Scopedptr<T>& operator=(const Scopedptr<T>& p);
private:
T* _ptr;
};
void SPTest()
{
Scopedptr<int> sp(new int(10));
cout << "sp = " << *sp << endl;
}
scoped_ptr中对拷贝构造函数和赋值运算符的重载函数只是进行了声明,并没有去定义这两个函数,而且声明为protected或者是private,这是防止别人在类外对这两个函数进行定义。防止拷贝,所以说scope_dptr是一种简单粗暴的方式。
shared_ptr
编写程序往往要用到拷贝,这样scopedptr就不能起到相应的作用,所以便有了shared_ptr。
shared_ptr->采用了引用计数,优点是功能强大,但是也有缺点,缺点是过于复杂,而且会引起循环引用。
template<class T>
class Sharedptr
{
public:
Sharedptr(T* ptr = NULL)
:_ptr(ptr)
, pcount(0)
{
if (_ptr != NULL)
pcount = new int(1);
}
Sharedptr(const Sharedptr<T>& sp)
{
_ptr = sp._ptr;
pcount = sp.pcount;
++(*pcount);
}
Sharedptr<T>& operator=(const Sharedptr<T>& sp)
{
if (this != &sp)
{
if (_ptr && --(*pcount) == 0)
{
delete pcount;
delete _ptr;
}
pcount = sp.pcount;
_ptr = sp._ptr;
++(*pcount);
}
return *this;
}
~Sharedptr()
{
if (_ptr && --(*pcount) == 0)
{
delete _ptr;
delete pcount;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T Getpcount()
{
return *(pcount);
}
void Realease()
{
if (--(*pcount) == 0)
{
delete _pcount;
delete _ptr;
}
}
void Reset(T* ptr, int* pcount)
{
if (_ptr != ptr)
{
delete _ptr;
delete _pcount;
}
_ptr = ptr;
_pcount = pcount;
}
private:
T* _ptr;
T* pcount;
};
void Test()
{
Sharedptr<int> s1(new int(10));
cout << "s1 = " << *s1 << endl;
cout << "pcount = " << s1.Getpcount() << endl;
Sharedptr<int> s2(s1);
cout << "s2 = " << *s2 << endl;
cout << "pcount = " << s2.Getpcount() << endl;
Sharedptr<int> s3;
s3 = s1;
cout << "s3 = " << *s3 << endl;
cout << "pcount = " << s3.Getpcount() << endl;
}
测试结果: