关于什么是shared_ptr这里就不赘述了,废话少说直接开始!
目录
一、类大纲
智能指针本质就是把指针封装起来,因此成员中必然有那个被封装的指针。因为是任意类型的指针,所以使用模板类。
shared_ptr需要对指向每个对象的shared_ptr计数,因此选择使用一个int指针来作为这个计数变量。(指向同一对象的shared_ptr们要共享,但是指向不同对象的不能共享,因此不能选用静态变量来完成这个工作)
基于同上的原因,使用一个mutex指针来当做指向同一对象的shared_ptr们的锁。这个锁的存在就是为了保证上面的int*的安全,所以它和int*是成对出现的。
template <class T>
class mySharedPtr
{
private:
T* _ptr; //被封装的指针
int* _pcount; //当shared_ptr指向同一对象时共享的那个计数
mutex* _pmtx; //当shared_ptr指向同一对象时共享的锁
public:
//构造函数
mySharedPtr(T* ptr); //构造函数
mySharedPtr(const mySharedPtr<T>& sp) //拷贝构造函数
//功能函数
void release(); //解除绑定
void addCount(); //增加计数
void showCount(); //显示计数(非必须)
//运算符重载
mySharedPtr& operator=(const mySharedPtr<T> &sp); //= 赋值重载
T& operator*(); //* 指针解引用重载
T* operator->(); //-> 指针访问重载
//析构函数
~mySharedPtr();
};
二、构造函数和析构函数
1.利用指针构造
以一个普通指针为基构造需要new出计数变量和锁。
(这里引出一个内存的问题:这里new出来的int和mutex作用域是怎样的?如果new它的那个shared_ptr被析构了会发生什么?突然想到new出来的当然是在堆上啊……)
//此处是类内实现的写法
mySharedPtr(T* ptr)
:_ptr(ptr)
,_pcount(new int(1))
,_pmtx(new mutex)
{}
2.拷贝构造
以另一个shared_ptr为基构造。赋值另一个的各个变量并且增加计数。
(这里又出现一个权限的问题。按理说_ptr/_pcount/_pmutex这三个变量都是私有的,为什么可以直接使用?答:私有的定义是同一类哪怕不是同一个对象都可以互相直接访问,但是别的类不可以。测试过了是这样的。)
//此处是类内实现的写法
mySharedPtr(const mySharedPtr<T>& sp)
:_ptr(sp._ptr)
,_pcount(sp._pcount)
,_pmtx(sp._pmtx)
{
addCount(); //因为和被拷贝的shared_ptr指向同一个对象,因此增加计数。
}
3.析构函数
为什么这里应当release()?假如我们定义了一个shared_ptr,现在它的作用域结束了,要析构了,如果你不release的话,别的指向同一个对象的shared_ptr的计数就不会减少,如此便造成了错误。
//此处是类内实现的代码
~mySharedPtr()
{
release(); //析构时应当解绑
}
三、功能函数
1.release()
将此shared_ptr与目前绑定的指针解绑。如果解绑后该指针没有shared_ptr指向它,就把mutex和int delete掉。
template<class T>
void mySharedPtr<T>::release(){
_pmtx->lock();
bool deleteflag=false;
//锁不能在lock时delete
//而在lock后不一定需要delete
//所以一个标志位告诉解锁后需不需要delete mutx
if(--(*_pcount)==0){
cout<<"delete:"<<*_ptr<<endl;
delete _ptr;
delete _pcount;
deleteflag=true;
}
_pmtx->unlock();
if(deleteflag==true){
delete _pmtx;
}
}
2.addCount()
当调用这个函数时,增加计数。拷贝构造和赋值时会用到。
template<class T>
void mySharedPtr<T>::addCount(){
_pmtx->lock();
++(*_pcount);
_pmtx->unlock();
}
3.showCount()
纯调试函数,写不写不影响。
template<class T>
void mySharedPtr<T>::showCount(){
cout<<(*_ptr)<<" "<<(*_pcount)<<endl;
}
四、运算符重载
1.赋值(=)重载
要记得防止自我赋值。返回值是一个共享指针对象的引用。
template<class T>
mySharedPtr<T>& mySharedPtr<T>::operator=(const mySharedPtr<T>& sp){
if(_ptr!=sp._ptr){//防止自我赋值
release();
_ptr=sp._ptr;
_pcount=sp._pcount;
_pmtx=sp._pmtx;
addCount();
}
return *this;
}
2.指针解引用(*)重载
用这个符号就是想对我们封装的那个指针指向的对象操作,所以直接返回就好了。返回值是封装的那个指针指向的类型的引用。
template<class T>
T& mySharedPtr<T>::operator*(){
return *_ptr;
}
3.指针访问(->)重载
返回值是一个指针。(其实有一点不懂这里,理论上当使用->时,我们的目的是想要使用被封装的那个指针->,这里只是简单的返回了一个指针。不懂。)
template<class T>
T* mySharedPtr<T>::operator->(){
return _ptr;
}
五、测试
int main()
{
mySharedPtr<char> a(new char('a'));
a.showCount();
mySharedPtr<char> b(a);
b.showCount();
mySharedPtr<char> c(new char('b'));
c.showCount();
c=b;
c.showCount();
return 0;
}
结果:
符合预期。最后还有一个delete:a是因为程序结束a的shared_ptr也都析构了。