智能指针即使用跟普通指针一样,
但是拥有自动回收内存的能力。
利用C++中的局部对象的构造析构特性,实现RAII。
简单的int类型的智能指针实现
class smp
{
public:
explicit smp(int *q = nullptr):p(q)
{}
~smp()
{
delete p;
}
private:
int *p;
};
// ...
smp sp(new int(10));
*sp = 5;
std::cout << *sp << std::endl;
还需要重载->
符号,访问数据中的其他成员。
struct node {
explicit node(int v = 3):left(nullptr),right(nullptr)
{}
struct node *left;
struct node *right;
int value;
};
class node_smp {
public:
explicit node_smp(node *q = nullptr):p(q)
{}
~node_smp()
{
delete p;
}
node& operator*()
{ return *p;}
node* operator->()
{ return p;}
public:
node *p;
};
node_smp nsmp(new node(10));
nsmp->value = 20;
std::cout << nsmp->value << std::endl;
template<typename T>
class smart_ptr {
public:
explicit smart_ptr(T *p = nullptr):q(p)
{}
~smart_ptr()
{
delete q;
q = nullptr;
}
T& operator *()
{ return *q;}
T* operator->()
{
return q;
}
private:
T *q;
};
smart_ptr<node> smp(new node(10));
smp->value = 100;
std::cout << smp->value << std::endl;
在上面代码的基础上禁止资源的转移,就是unique_ptr
的雏形啦。
template<typename T>
class unique_ptr_ {
public:
explicit unique_ptr_(T *p = nullptr):ptr(p)
{}
~unique_ptr_()
{
delete ptr;
}
unique_ptr_(const unique_ptr_& p) = delete;
unique_ptr_& operator=(const unique_ptr_& p) = delete;
private:
T *ptr;
};
到目前为止,实际上我们并没有处理拷贝构造函数和赋值函数。
在一个返回内的智能指针,且不会在别的地方被引用。
也就是该智能指针无法在另外一个地方再使用。
那么怎么实现能使用多个智能指针对象呢,标准库实现的方式是引用计数。
而要对多个对象进行计数,且多个对象的销毁并不确定,所以只能在对象首次使用时使用在堆上的分配一个内存进行引用计数。
当最后一个只能对象销毁时,销毁资源。
template<typename T>
class shared_ptr_ {
public:
explicit shared_ptr_(T *data = nullptr):RefCnt(new int {0}) {
data_ = data;
*RefCnt = 0;
if (data_) {
++*RefCnt;
}
}
~shared_ptr_() {
--*RefCnt;
if (*RefCnt == 0) {
delete data_;
delete RefCnt;
}
}
shared_ptr_& operator=(const shared_ptr_ & p) {
if ( *this != p) {
RefCnt = p.RefCnt;
data_ = p.data_;
++*RefCnt;
}
return *this;
}
shared_ptr_(const shared_ptr_ &p) {
data_ = p.data_;
RefCnt = p.RefCnt;
++*RefCnt;
}
T& operator*()
{
return *data_;
}
T* operator->()
{
return data_;
}
private:
T *data_;
int *RefCnt;
};
在使用循环链表的过程中即存在这种情况:
template<typename T>
class shared_ptr_ {
public:
explicit shared_ptr_(T *data = nullptr):data_(data),RefCnt(new int {0}){
if (data_) {
++*RefCnt;
}
}
~shared_ptr_() {
if (*RefCnt) {
--*RefCnt;
if ( *RefCnt )
std::cout << "current ref: "<< *RefCnt << std::endl;
}
if (*RefCnt == 0) {
delete data_;
delete RefCnt;
}
}
shared_ptr_<T>& operator=(const shared_ptr_<T> & p) {
if ( this != &p) {
RefCnt = p.RefCnt;
data_ = p.data_;
if (*RefCnt) {
++*RefCnt;
}
}
return *this;
}
shared_ptr_(const shared_ptr_ &p) {
data_ = p.data_;
RefCnt = p.RefCnt;
if (*RefCnt)
++*RefCnt;
}
T& operator*()
{
return *data_;
}
T* operator->()
{
return data_;
}
private:
T *data_;
int *RefCnt;
};
class List {
public:
explicit List(int _v = 0):v(_v), pPre(), pNext()
{}
~List()
{
std::cout << "delete List " << v << std::endl;
}
int v;
shared_ptr_<List> pPre;
shared_ptr_<List> pNext;
};
int main()
{
shared_ptr_<List> p1(new List(2));
shared_ptr_<List> p2(new List(3));
p1->pPre = p2;
p2->pPre = p1;
p1->pNext = p2;
p2->pNext = p1;
return 0;
}
输出
current ref: 2
current ref: 2
由于实际上在链表中,并不实际占有资源而只是一个引用。所以会出现程序运行后还剩两个计数。
即对于p1
,还有p1->pPre
和p1->next
两个指针计数。
更好的实现就要看boost
和标准库这些的相关实现了。