虽然auto_ptr已经被C++11弃用了,但是通过分析它的代码仍然能够学习到一些知识。
auto_ptr的动机:动态开辟内存的时候,new完之后必须delete,否则会产生内存泄漏。但是如果程序在delete之前发生异常,那么delete语句就不能执行,就会产生内存泄漏。auto_ptr技术正是为了解决这一问题而存在的。首先需要引入包含其的头文件#include< memory >。智能指针本质上实际是一个对象,但它重载了"*“和”->"运算符。对象在销毁的时候,会自动调用析构函数。所以智能指针能实现的关键在此。
下面简单演示一下auto_ptr的用法:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
int main(){
int *p = new int(10);
auto_ptr<int>pa(p);
cout<<*pa<<endl;//10
*pa = 100;
cout<<*pa<<endl;//100
auto_ptr<string>ps(new string("hello world"));
cout<<ps->size()<<endl;//11
}
智能指针的关键在于拥有权的转移
拷贝构造和赋值的时候,要进行拥有权转移,保证一个空间只由一个对象拥有。
下面是VC++6.0中auto_ptr的实现:
#include <iostream>
#include <string>
#include <memory>
//using namespace std;
//智能指针 //自动调用 业务理解
//拥有权的转移
template<class _Ty>
class auto_ptr
{
public:
explicit auto_ptr(_Ty *_P = 0)
: _Owns(_P != 0), _Ptr(_P)
{}
//auto_ptr<int> pa1 = pa;
//const auto_ptr<_Ty> * _Y
auto_ptr(const auto_ptr<_Ty> &_Y)
: _Owns(_Y._Owns), _Ptr(_Y.release())
{}
//auto_ptr<int> pa1;
//pa1 = pa;
auto_ptr<_Ty> &operator=(const auto_ptr<_Ty> &_Y)
{
if (this != &_Y) {
if (_Ptr != _Y._Ptr) {
if (_Owns)
delete _Ptr;
_Owns = _Y._Owns;
}
else if (_Y._Owns)
_Owns = true;
_Ptr = _Y.release();
}
return (*this);
}
~auto_ptr()
{
if (_Owns)
delete _Ptr;
}
public:
_Ty &operator*() const
{
return *_Ptr;
}
_Ty *operator->() const
{
return _Ptr;
}
_Ty *release() const
{
((auto_ptr<_Ty> *) this)->_Owns = false;
return _Ptr;
}
private:
bool _Owns;
_Ty *_Ptr;
};
int main()
{
int *p = new int(10);
int *p1 = new int(20);
auto_ptr<int> pa(p);
//........
pa.release();
auto_ptr<int> pa1(p);
pa1 = pa;
}
当pa2 = pa1时 :
1.pa2 无指向 : 将pa1的指针给pa2赋值,再将pa1将拥有权进行转移;
2.pa2 有指向不同于pa1的对象 : 将pa2的指向进行析构,再将pa1的指针给pa2赋值,将pa1的拥有权转移;
3.pa2 有指向和pa1相同的对象 : 判断pa1是否有拥有权,有则将pa1的拥有权转移给pa2。
但vc版的auot_ptr也有一些问题,就是将拥有权转移后,除了不能够对其多次析构外,还可以对其进行操作,这就不好了,你都已经分手了,还不放手。在VS版本上对拥有权有更好的管理。如果两个指针指向一个空间但是都没有拥有权,最后需要用delete释放。
下面是VS中的auto_ptr的实现:
//假设全局pa2都是用pa1来构造
//如:pa2(pa1)、pa2=pa1
template<class _Ty> //auto_ptr类声明
class auto_ptr;
template<class _Ty>
struct auto_ptr_ref //auto_ptr的辅助类
{
explicit auto_ptr_ref(_Ty *_Right): _Ref(_Right)
{}
_Ty *_Ref;
};
template<class _Ty>
class auto_ptr//auto_ptr类
{
public:
typedef auto_ptr<_Ty> _Myt; //管理类的类型
typedef _Ty element_type; //被管理元素的类型
explicit auto_ptr(_Ty *_Ptr = 0) : _Myptr(_Ptr) //构造
{} //从原始指针中获取控制权
//若pa2(pa1);
auto_ptr(_Myt& _Right) : _Myptr(_Right.release()) //拷贝构造
{}
//若:pa2(pa1)
auto_ptr(auto_ptr_ref<_Ty> _Right)
{
// 用右值来构造
_Ty *_Ptr = _Right._Ref; //将pa1的指针保存在Ptr中
_Right._Ref = 0; // 将pa1的指针指向空
_Myptr = _Ptr; // 将pa2的地址指向pa1的地址
}
template<class _Other>
operator auto_ptr<_Other>() //转换为可转换的类型
{
return (auto_ptr<_Other>(*this));
}
template<class _Other>
operator auto_ptr_ref<_Other>() //隐式类型转换,将auto_ptr类型指针转换为auto_ptr_ref类型
{
_Other *_Cvtptr = _Myptr;
auto_ptr_ref<_Other> _Ans(_Cvtptr);
_Myptr = 0;
return (_Ans);
}
template<class _Other>
_Myt& operator=(auto_ptr<_Other>& _Right) //针对可转换为 _Ty* 类型的 _Other* 类型的拷贝函数
{
reset(_Right.release());
return (*this);
}
template<class _Other> //特定类型构造函数
auto_ptr(auto_ptr<_Other>& _Right) : _Myptr(_Right.release())
{} //针对可转换为 _Ty* 类型的 _Other* 类型的构造函数
//若:pa2=pa1
_Myt& operator=(_Myt& _Right) //拷贝赋值函数
{
reset(_Right.release());
return (*this);
}
//若:pa2=pa1
_Myt& operator=(auto_ptr_ref<_Ty> _Right) //将一个 auto_ptr_ref 类型的变量赋值给 *this
{
_Ty *_Ptr = _Right._Ref; //将pa1的Ref指针赋值给pa2的_Ptr指针
_Right._Ref = 0; // 将pa1的Ref指针指向空
reset(_Ptr); // 将pa2的_Myptr指向pa1
return (*this);
}
~auto_ptr() //析构
{
delete _Myptr; //析构指针所指向的内容
}
_Ty& operator*() const //重载*
{
if (_Myptr == 0)
{
_DEBUG_ERROR("auto_ptr not dereferencable");
}
return (*get());
}
_Ty *operator->() const //重载->
{
return (get());
}
_Ty *get() const //用来返回_Myptr的地址
{
return (_Myptr);
}
_Ty *release() //将一个对象的_Myptr记住,再指向空,然后再把其记住的地址返回
{
_Ty *_Tmp = _Myptr; //先将pa1的指针用Tmp保存,
_Myptr = 0; //再讲pa1的指针指向空
return (_Tmp); // 返回Tmp即原来pa1指向的地址
}
void reset(_Ty *_Ptr = 0) //将this的_Myptr指向传进来的指针的地址
{
if (_Ptr != _Myptr) //如果pa1“以前的”(pa1在release()中被指向空,就是返回的那个Tmp用来记住pa1以前的那个地址)那个地址所指向的地址不等于pa2的地址
delete _Myptr; //先析构pa2所指向的地址
_Myptr = _Ptr; //再将pa2地址指向返回来的pa1的老地址
}
private:
_Ty *_Myptr;
};
PS:这篇博文讲的特别好auto_ptr源码剖析