上一篇文章介绍了智能指针的基本概念及boost库里基本的智能指针,这里主要模拟实现库里面的智能指针(简单实现)。https://blog.csdn.net/xu1105775448/article/details/80625936
1.auto_ptr具有RAII和像指针一样的特点。
2.模拟实现:
template
class Auto_Ptr
{
public:
//RAII
Auto_Ptr(T* ptr)
:_ptr(ptr)
{}
~Auto_Ptr()
{
delete _ptr;
}
//像指针一样
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
void TestAutoPtr()
{
int *ptr = new int(10);
Auto_Ptr<int> p(ptr);
/**p = 20;
cout << *p << endl;*/
Auto_Ptr<int> p1(p);
}
3.但是如果只写上面的代码就会有一个问题,当我们想将一个AutoPtr赋值(或拷贝构造)给另一个AutoPtr,我们没写它的拷贝构造和赋值运算符重载,就会调用编译器的,这是浅拷贝,就会导致空间被多次释放。
4.auto_ptr思想是:管理权的转移。也就是当发现有一个auto_ptr对象a1赋值给另一个对象a2或者拷贝构造a2,那么a1获得的资源就交给a2,a2来管理a1指向的那块空间,a1就指向NULL,这样就保证了每块空间只有一个auto_ptr对象指向它,就不会有空间被释放多次的问题。
5.自己模拟实现完善版本的:
template<class T>
class AutoPtr
{
public:
//构造函数获得资源
AutoPtr(T* ptr)
:_ptr(ptr)
{}
//管理权转移
AutoPtr(AutoPtr& ap) //参数不能加const
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
AutoPtr& operator=(AutoPtr& ap)
{
if (this != &ap)
{
//先释放自己的
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
//析构函数清理资源
~AutoPtr()
{
if (_ptr != NULL)
{
delete _ptr;
printf("0X%p\n", _ptr);
}
}
//像指针一样
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
};
void TestAutoPtr()
{
AutoPtr<int> ap1(new int(10));
AutoPtr<int> ap2(new int(20));
ap1 = ap2;
}
6.auto_ptr管理权的另一种实现方式:(增加一个bool类型的owner,作为一个标志,如果我管理这块空间,那么我的owner就为true,没有管理就为false);但是这种方式没有上面直接将指向的空间置空好,若ap1拷贝构造ap2,那么这里的所有权就在ap2上,即ap2的owner为true,ap1的owner为false,但是如果ap2的生命周期比ap1短就有问题。例如函数传参时,当用ap2传给一个函数fun,而fun得参数为一个auto_ptr对象ap,就会将ap2得管理权转移给ap,当ap出了作用域就会调用其析构函数,就会将ap指向的空间释放掉,就会使得再想访问ap2就会出错。
template<class T>
class AutoPtr
{
public:
//构造函数获得资源
AutoPtr(T* ptr)
:_ptr(ptr)
,_owner(true)
{}
//管理权转移
AutoPtr(AutoPtr& ap)
:_ptr(ap._ptr)
{
ap._owner = false;
_owner = true;
}
AutoPtr& operator=(AutoPtr& ap)
{
if (this != &ap)
{
//先释放自己的
delete _ptr;
_ptr = ap._ptr;
ap._owner = false;
_owner = true;
}
return *this;
}
//析构函数清理资源
~AutoPtr()
{
if (_owner == true)
{
delete _ptr;
printf("0X%p\n", _ptr);
}
}
//像指针一样
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
bool _owner;
};
7.但是autoptr有个很大的缺点,当将ap1的管理权交给ap2后,就会使得ap1指向一块空的空间,当需要解引用ap1时,就是解引用空指针。
1.scoped_ptr也要有RAII和像指针一样的特点,它采用防拷贝的方式。(防拷贝:将拷贝构造和赋值运算符是声明实现,并且声明为私有,因为我们自己声明,就不会调用系统自动生成的,而且如果只声明,就会有人在类外定义,所以必须声明为私有)
2.模拟实现:
template
class ScopedPtr
{
public:
//RAII
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
~ScopedPtr()
{
if (_ptr)
{
delete _ptr;
}
}
//像指针一样
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
public:
//将拷贝构造和赋值运算符重载声明为私有
ScopedPtr(const ScopedPtr& sp);
ScopedPtr operator=(const ScopedPtr& sp);
protected:
T* _ptr;
};
void TestScopedPtr()
{
ScopedPtr<int> sp(new int(10));
ScopedPtr<int> sp1(sp);
}
1.scoped_ptr用于管理new单个对象的空间的释放,而scoped_array用于管理new多个对象的释放(即调用new[]),而且scoped_array里面不支持operator*和operator->。对数据的访问采用[](像数组一样,通过下标的形式)。
2.模拟实现:
template
class ScopedArray
{
public:
//RAII
ScopedArray(T* ptr)
:_ptr(ptr)
{}
~ScopedArray()
{
if (_ptr)
{
delete[] _ptr;
}
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
public:
ScopedArray(const ScopedArray& sp);
ScopedArray operator=(const ScopedArray& sp);
protected:
T* _ptr;
};
void TestScopedArray()
{
ScopedArray<int> sp1(new int[10]);
}
1.shared_ptr为共享指针,意味我们共同指向一块空间;里面采用引用计数,当有别的shared_ptr指向我这块空间时,就增加引用计数,当引用计数减为0的时候,才释放这块空间。
2.模拟实现:
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}
SharedPtr(const SharedPtr& ap)
:_ptr(ap._ptr)
, _pCount(ap._pCount)
{
++(*_pCount);
}
SharedPtr& operator=(const SharedPtr& ap)
{
if (_ptr != ap._ptr)
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
_ptr = ap._ptr;
_pCount = ap._pCount;
++(*_pCount);
}
return *this;
}
~SharedPtr()
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
cout << "~SharedPtr()" << endl;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
int* _pCount;
};
void TestSharedPtr()
{
SharedPtr<int> sp1(new int(10));
SharedPtr<int> sp2(sp1);
sp1 = sp2;
}
3.但是shared_ptr会出现一个问题,就是循环引用,就会导致引用计数一直无法减到0,就会导致空间没有释放。例如如下代码:
template<class T>
class SharedPtr;
template<class T>
struct ListNode
{
T _data;
SharedPtr<ListNode<T>> _next;
SharedPtr<ListNode<T>> _prev;
ListNode(T data)
:_data(data)
, _next(NULL)
, _prev(NULL)
{}
};
//中间部分与上面SharedPtr代码一样
void TestSharedPtr()
{
SharedPtr<ListNode> sp1(new ListNode<int> (10));
SharedPtr<ListNode> sp2(new ListNode<int>(20));
sp1->_next = sp2;
sp2->_prev = sp1;
}
分析:
template<class T>
class WeakPtr
{
public:
WeakPtr(SharedPtr<T> sp)
:ptr(sp._ptr)
{}
WeakPtr(const WeakPtr<T>& sp)
:ptr(sp.ptr)
{}
WeakPtr<T>& operator=(const WeakPtr<T>& wp)
{
if (this != &wp)
{
ptr = wp.ptr;
}
return *this;
}
protected:
T* ptr;
};
1.shared_ptr用于管理单个对象的释放,而shared_array用于管理多个对象的释放。对于scoped_array来说,它不支持*和->,它支持[]的形式访问。
2.模拟实现:
template<class T>
class SharedArray
{
public:
SharedArray(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}
SharedArray(const SharedArray& sa)
:_ptr(sa._ptr)
, _pCount(sa._pCount)
{}
SharedArray& operator=(const SharedArray& sa)
{
if (_ptr != sa._ptr)
{
if (--(*_pCount) == 0)
{
delete[] _ptr;
delete _pCount;
}
_ptr = sa._ptr;
_pCount = sa._pCount;
++(*_pCount);
}
return *this;
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
~SharedArray()
{
if (--(*_pCount) == 0)
{
delete[] _ptr;
delete _pCount;
}
}
protected:
T *_ptr;
int* _pCount;
};
1.对于C++11来说,它的库里面不包含shared_array和scoped_array,所以当new[]时,必须要依靠别的方式才能按照delete[]的形式释放。C++11和boost里面都包含定址删除器,当我以何种方式动态开辟的空间,我就要以什么样的方式去释放空间。(对于new[]就要delete[],对于fopen就要fclose,对于lock就要unlock)
2.当自己模拟实现的时候,对于shared_ptr来说,可以给它两个模板参数,一个表示指针的类型,一个表示采用何种方式删除。
3.定址删除器就是一个仿函数,通过函数对象调用重载的operator()函数,里面是delete,就调用delete,里面是delete[]就是delete[]。
4.模拟实现:
(1)首先包含仿函数,这里定义了3个仿函数,一个是专门用来进行delete,一个用来delete[],一个用来fclose;
template<class T>
struct Delete
{
void operator()(T* ptr)
{
delete ptr;
}
};
template<class T>
struct DeleteArray
{
void operator()(T* ptr)
{
delete[] ptr;
}
};
struct Fclose
{
void operator()(FILE* fp)
{
fclose(fp);
}
};
(2)前面实现的SharedPtr进一步改进,增加一个模板参数,用来管理指针的释放。增加一个模板参数D,给一个缺省参数为Delete,当我们不适用第二个模板参数表示采用delete的方式,当我们给定第二个模板参数之后,我们使用哪个模板参数,就调用哪个仿函数。
template<class T,class D = Delete>
class SharedPtr
{
friend class WeakPtr;
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}
SharedPtr(T* ptr, D d)
:_ptr(ptr)
, _d(d)
, _pCount(new int(1))
{}
SharedPtr(const SharedPtr& ap,D d)
:_ptr(ap._ptr)
, _pCount(ap._pCount)
, _d(d)
{
++(*_pCount);
}
SharedPtr& operator=(const SharedPtr& ap)
{
if (_ptr != ap._ptr)
{
if (--(*_pCount) == 0)
{
_d(_ptr); //_d为仿函数对象,通过()就可以调用对应的函数
delete _pCount;
}
_ptr = ap._ptr;
_pCount = ap._pCount;
++(*_pCount);
}
return *this;
}
~SharedPtr()
{
if (--(*_pCount) == 0)
{
if (_ptr)
{
_d(_ptr);
delete _pCount;
cout << "~SharedPtr()" << endl;
}
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
protected:
T* _ptr;
int* _pCount;
D _d;
};
void TestSharedPtr()
{
DeleteArray<string> da;
SharedPtr<string,DeleteArray<string>> sp(new string[10],da);
SharedPtr<int> sp1(new int(10));
SharedPtr<string, DeleteArray<string>> sp2(new string[20]);
/*SharedPtr sp2(fopen("text.txt", "r"), Fclose());
SharedPtr sp3(fopen("text.txt", "w"));*/
}