在上一篇博客中我们说了下模拟实现AutoPtr,ScopedPtr
在这篇博客我们会来谈到模拟实现SharedPtr
首先我们先来一个小小的区分三个智能指针
1,AutoPtr:管理权的转移(严重缺陷,尽量不要使用)
2,ScopedPtr: 简单粗暴—防拷贝(只声明不定义)
3,SharedPtr:共享,引用计数,功能强大,循环引用,但是较为复杂
下面我们来模拟实现一下SharedPtr
template<class T>
class SharedPtr
{
public:
SharedPtr(T*_ptr)
:_ptr(ptr)
, _refCount(refCount)
{}
~SharedPtr()
{
Release(); //引用计数
}
inline void Release()
{
if (--*_refCount == 0) //引用计数,并判断如果为最后一个管理对象则释放
{
delete _ptr;
delete _refCount;
}
}
T &operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
, _refCount(sp._refCount)
{
(*_refCount)++;
}
SharedPtr<T>&operator=(const SharedPtr<T>&sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
protected:
T*_ptr;
T*_refCount;
};
这是我们初步模拟实现的SharedPtr
我们简单分析一下下面这段复制运算符的重载
复制运算符重载分为三种情况
1,自己给自己赋值。
2,两个对象管理同一空间。
3,两个对象管理不同空间。
其中1,2可以归纳为一类:都是管理同一空间
SharedPtr<T>&operator=(const SharedPtr<T>&sp)
{
if (_ptr != sp._ptr)//解决了1,2问题
{
Release();
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
但是在这里有一个问题:如果我们要实现
SharedPtr<string>sp1<new string[20]>
我们可以实现吗?
我们知道在C++中malloc/free,new/delete,new[ ]/delete[ ],fopen/fclose这些都是要成对出现的
我们来看一下delete与delete[ ]的区别
所以我们malloc/free,new/delete,new[ ]/delete[ ],fopen/fclose这些一定要成对出现,否则会造成不必要的麻烦
那么我们要解决这个问题,就引入了一个新的函数—仿函数。
仿函数的概念:仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
它是怎么应用的呢?
在C++里,我们通过在一个类中重载括号运算符的方法使用一个函数对象而不是一个普通函数。
我们先来简单的看一个小例子介绍一下
template<T>
struct less
{
bool operator()(const T&l,constT&r)
return l<r
};
int main()
{
int i1=10;
int i2=12;
cout<<(i1<i2)<<endl;//原始写法
less<int>less;
cout<<less(i1,i2)<<endl;//使用了仿函数
}
下来我们将仿函数应用于我们的SharedPtr中
template<class T>
class DeleteArray
{
public:
void operator()(T*ptr)
{
delete[] ptr;
}
};
template<class T>
class Delete
{
public:
void operator()(T*ptr)
{
delete ptr;
}
};
template<class T,class Del>
class SharedPtr
{
public:
SharedPtr(T*_ptr)
:_ptr(ptr)
, _refCount(refCount)
{}
~SharedPtr()
{
Release();
}
inline void Release()
{
if (--*_refCount == 0)
{
/*delete _ptr;*/
delete _refCount;
_del(_ptr);
}
}
T &operator*()
{
return *_ptr;
}
T*operator->()
{
return _ptr;
}
SharedPtr(const SharedPtr<T,Del>& sp)
:_ptr(sp._ptr)
, _refCount(sp._refCount)
{
(*_refCount)++;
}
SharedPtr<T, Del >&operator=(const SharedPtr<T,Del>&sp)
{
if (_ptr != sp._ptr)
{
Release();
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
protected:
T*_ptr;
T*_refCount;
T _del;
};
这样写出来我们就可以释放开辟的数组内存了
写的时候一定要像下面的一样,把缺省参数写全
SharedPtr<string DeleteArray<string>sp2<new string[20]>>
我们在上一篇博客说到了AutoPtr,ScopedPtr智能指针的缺点,其实SharedPtr也是有缺点的:
1,AutoPtr:管理权的转移(严重缺陷,尽量不要使用)
2,ScopedPtr: 简单粗暴—防拷贝(只声明不定义)——功能不全
3,SharedPtr:共享,引用计数,功能强大——缺点:循环引用。
先说一下SharedPtr是我们自己模拟实现的,库里面自己的命名是shared_ptr
那么shared_ptr的循环引用问题是要配合weak_ptr配合解决的
下面我们总结一下库里面:
1,auto_ptr:管理权的转移(带有缺陷的设计)—–C++93/03
2,scoped_ptr(boost),unique_ptr(C++11): 防拷贝(只声明不定义)—–简单粗暴的设计——功能不全
3,shared_ptr(boost/C++11):引用计数—–功能强大(支持拷贝,支持定制删除器)——缺点:循环引用。
注:boost是我们的第三方库具体请了解http://www.boost.org/