前面我们介绍智能指针的时候,说了两个智能指针分别是:auto_ptr和scoped_ptr,auto_ptr有问题,为此,scoped_ptr的出现解决了这个问题,scoped_ptr太霸道,不允许别人和他共用一块内存空间,所以,我们还得想办法解决这个问题。
回想我们以前看过内容,当提到共用一块内存空间的时候,我们会想到什么?
当然是深拷贝和浅拷贝了,最后我们是不是有给出了一个写时拷贝,就是通过引用计数的方法来实现共用一块内存空间的。对于这个问题,C++中也给出了一个相同方法的解决方案,这就是shared_ptr。
首先,我们还是模拟一下shared_ptr:
template <typename T>
class SharedPtr
{
public:
SharedPtr(T*ptr)
:_Ptr(ptr)
, _pcount(NULL)
{
if (_ptr)
{
_pcount=new int(1);
}
}
SharedPtr(const SharedPtr<int> & sp)
{
_ptr = sp._ptr;
_pcount = sp._pcount;
++(*_pcount);
}
~SharedPtr()
{
if (0 == (--*_pcount))
{
delete _ptr;
delete _pcount;
}
}
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
if (this != &sp)
{
if (_ptr != nullptr)
{
if (_pcount == 0)
{
delete _ptr;
delete _pcount;
}
}
else
{
_ptr = sp._ptr;
_pcount = sp._pcount;
++(*_pcount);
}
}
return *this;
}
T&operator*()
{
return *_ptr;
}
T*operator->()
{
return *_ptr;
}
private:
T* _ptr;
int * _pcount;
};
定制删除器
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//为解决文件指针
struct Fclose
{
void operator()(FILE*& fp)
{
cout<< "Fclose()" << endl;
fclose(fp);
fp = NULL;
}
};
//为解决malloc开辟的空间
template<class T>
struct Free
{
void operator()(T*& p)
{
free(p);
p = NULL;
}
};
//一般情况下(使用new动态开辟的空间)
template<class T>
class Delete
{
public :
void operator()(T*& p)
{
delete p;
p = NULL;
}
};
template<class T, class Destory = Delete<T> >
class SharedPtr
{
public :
SharedPtr(T* ptr = 0)//构造函数
:_ptr(ptr)
,_pCount(NULL)
{
if (_ptr)
{
_pCount = new int(1);
}
}
SharedPtr(const SharedPtr<T>& sp)//拷贝构造函数
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
if (_ptr)
{
++GetRef();
}
}
//sp1 = sp2
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
//可有三种情况:①sp1._ptr = NULL ②sp1的引用计数为1 ③sp1的引用计数大于1
if (this != &sp)
{
Release();
_ptr = sp._ptr;
_pCount = sp._pCount;
++GetRef();
}
return *this;
}
//辅助函数
void Release()
{
if (_ptr && --GetRef() == 0)
{
Destory()(_ptr);
delete _pCount;
_pCount = NULL;
}
}
//析构函数
~SharedPtr()
{
Release();
}
int& GetRef()
{
return *_pCount;
}
private:
T* _ptr;
int* _pCount;
};
void Test2()
{
FILE* sp1 = fopen("test.txt", "rb");
SharedPtr<FILE, Fclose> sp2(sp1);
int* sp3 = (int*)malloc(sizeof(int));
SharedPtr<int, Free<int> >sp4(sp3);
int* sp5 = new int(1);
SharedPtr<int> sp6(sp5);
}
int main()
{
Test2();
return 0;
}
循环引用
通过引用计数,我们解决了智能指针共享一块空间的问题,不过,shared_ptr还是有点问题,那就是循环引用的问题,什么是循环引用的问题呢?先来看一个例子:
struct Node
{
int _data;
shared_ptr<Node> _next;
shared_ptr<Node> _prev;
};
void FunTest()
{
shared_ptr<Node> sp1(new Node);
shared_ptr<Node> sp2(new Node);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1->_next = sp2;
sp2->_prev = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
我们先定义一个双链表节点,然后,我们再创建两个节点,并且让其链接起来,之后,我们再想要释放这两个节点的时候,会发现一个很有意思的现象,那就是,释放sp1的时候,必须先释放sp2,而想要释放sp2,那么必须先释放sp1,这就是循环引用的例子。
如图:
这是我画的两个节点,sp1的nest指向sp2,同时,sp2的prev指向sp1。那么sp1所指向的内存就被两个指针所管理,分别是:sp1和sp2->_prev;同样的,sp2所指向的内存空间就会被sp2和sp1->_nest所管理。
如果现在想要释放的是sp1,那么释放内存的时候其实是将其引用计数减去1,不是将这段空间释放。
那么,如何解决这个问题呢?
这里就不得不说:weak_ptr 了。