1、auto_ptr
智能指针形如其名,智能(可以自主删除在堆上开辟的空间),指针(可以和普通指针一样,可以由 * , -> 操作)。在内部,其实就是一个对象。
auto_ptr时最早的智能指针,最早是c98中的vc版本,再到vs版本,在还有boost库里的各种智能指针。我们首先介绍他的基本用法,然后进行模拟。
我们要注意,从C ++ 11开始不推荐使用此类智能指针。推荐使用 unique_ptr,是一种具有类似功能的新设施,但具有更高的安全性(无伪造副本分配),增加的功能(删除器)和对数组的支持。
void main()
{
int *tmp = new int(100);
auto_ptr<int> ap(tmp); //智能指针ap接管tmp,ap其实是一个对象,智能体现在对象由析构方法,不要用户析构
cout << *ap << endl; //输出100
auto_ptr<string> ap1(new string("hello world!"));
cout << ap1->size() << endl;
ap1.reset(new string("good ")); //重新设置ap1管理的对象
cout << ap1->size() << endl;
ap1.release(); //释放指向的对象的空间(vs c++11)/释放指向对象的所有权(vc c++98)
auto_ptr<string>ap2 (new string ("bad"));
ap1 = ap2;
cout << *ap1 << endl;
}
注意我们不能同时构造两个指向一个地址的智能指针,这样会导致程序崩溃,原因很简单,地址释放了两次。
void main()
{
string* s = new string("1234");
auto_ptr<string> ap1(s);
//auto_ptr<string> ap2(s); //程序出错
//以下不会出错,因为再将ap1赋值给ap2时,ap1的所有权变为false
auto_ptr<string> ap2(s);
ap2 = ap1;
}
但仍有一个问题是,*ap1还是=1234,说明auto_ptr还存在藕断丝连的问题。
1.1、模拟实现,vc版本
成员包括两个,一个时要指向的指针,一个是所有权_Owns(当_Owns==true),说明智能指针有效,相反,无效。
template<class _Ty>
class my_auto_ptr
{
public:
my_auto_ptr(_Ty *_P = 0) : _Owns(_P != 0), _Ptr(_P)//当_P != 0时,_Owns=1,_Ptr=_P
{}
my_auto_ptr(const my_auto_ptr<_Ty> &_Y) : _Owns(_Y._Owns), _Ptr(_Y.release())
{}
my_auto_ptr<_Ty>& operator =(const my_auto_ptr<_Ty>& _Y)
{
if (this != &_Y)
{
if (_Ptr != _Y.get()) //
{
if (_Owns) //当原来由空间时,要删除原有空间
delete _Ptr;
_Owns = _Y._Owns;
}
else if (_Y._Owns) //如果_Y._Owns为flase,则不改变_Owns的所有权
_Owns = true;
_Ptr = _Y.release();//删除原有的管理权,赋值给_Ptr
}
return (*this);
}
~my_auto_ptr()
{
if (_Owns)
delete _Ptr;
}
public:
_Ty& operator*()const
{
return *_Ptr;
}
_Ty* operator->()const
{
return _Ptr;
}
public:
_Ty* release()const //删除智能指针,将管理权变为false
{
//_Owns = false; 不行,是常方法,必须进行强制类型转换 或者使用 mutable关键字
//*((bool*)&_Owns) = false; //要么直接将const bool类型强转为bool类型
((my_auto_ptr<_Ty>*)this)->_Owns = false; //要么直接将const (my_auto_ptr<_Ty>*)this类型强转为(my_auto_ptr<_Ty>*) this类型 强制转换 忽略类型
return _Ptr;
}
_Ty* get()const
{
return _Ptr;
}
private:
bool _Owns;//管理权
_Ty *_Ptr; //指向这块地址的指针
};
唯一注意的是,在赋值运算符重载时,其实和我们常说的深拷贝的思想差不多,但是因为还有一个变量, _Owns 所有权,所以还需要增加对_Owns的判断。
1.2、模拟实现,vs版本
vs版本改进了vc版本,不存在藕断丝连的问题,只有一个私有成员。
template<class _Ty>
class my_auto_ptr
{
typedef my_auto_ptr<_Ty> _Myt;
public:
my_auto_ptr(_Ty *_Ptr = 0) : _Myptr(_Ptr)
{}
auto_ptr(_Myt& _Right) : _Myptr(_Right.release())
{}
_Myt& operator=(_Myt& _Right)
{
reset(_Right.release());//这一步相当于做了两件事,1、释放智能指针_Right 2、reset(_Right._Myptr) 将_Right._Myptr赋值给_Myptr
return (*this);
}
~my_auto_ptr()
{
delete _Myptr;
}
public:
_Ty& operator*() const
{
return (*get());
}
_Ty *release()
{
_Ty *_Tmp = _Myptr;
_Myptr = 0;
return (_Tmp);
}
_Ty *get() const
{
return (_Myptr);
}
void reset(_Ty *_Ptr = 0)
{
if (_Ptr != _Myptr)
delete _Myptr;
_Myptr = _Ptr;
}
private:
_Ty *_Myptr; //
};
2、unique_ptr
unique_ptr是c++11推荐使用的,与auto_ptr相比,unique_ptr不允许拷贝构造和赋值,将这两个成员方法变为私有。
void main()
{
string* s = new string ("1234");
unique_ptr<string> up(s);
cout << up->size() << endl;
//不允许拷贝构造和赋值
//unique_ptr<string> up2(up);
//unique_ptr<string> up3;
//up3 = up2;
}
unique_ptr也可以对数组进行操作,
#include <iostream>
#include <memory>
int main () {
std::unique_ptr<int[]> foo (new int[5]);
for (int i=0; i<5; ++i) foo[i] = i;
for (int i=0; i<5; ++i) std::cout << foo[i] << ' ';
std::cout << '\n';
return 0;
}
unique_ptr自己定制删除器,
template <class T>
class state_deleter { // a deleter class with state
public:
void operator()(T* p) {
std::cout << "[deleted[] #" << "]\n";
delete[] p;
}
};
void main()
{
unique_ptr<int, state_deleter<int>> sp2(new int[3], del);
}
2.1 模拟实现
这里,我们主要参考scoped_ptr来实现。
template<class T>
class My_unique_ptr
{
private:
T * px;
My_unique_ptr(My_unique_ptr const &);
My_unique_ptr & operator=(My_unique_ptr const &);
typedef My_unique_ptr<T> this_type;
void operator==(My_unique_ptr const&) const;
void operator!=(My_unique_ptr const&) const;
public:
typedef T element_type;
explicit My_unique_ptr(T * p = 0) : px(p) // never throws
{ }
~My_unique_ptr() // never throws
{
//boost::checked_delete(px);
delete px;
}
void reset(T * p = 0) // never throws
{
this_type(p).swap(*this);
}
T & operator*() const // never throws
{
return *px;
}
T * operator->() const // never throws
{
return px;
}
T * get() const // never throws
{
return px;
}
void swap(My_unique_ptr & b) // never throws
{
T * tmp = b.px;
b.px = px;
px = tmp;
}
};
在scoped_ptr源码中,其实还有以下两个函数,用于检查类型的完整度,因为c++11允许对不完整类型的delete,但如果class的析构函数有特定数据操作,或者具有自定义的operator delete成员函数,那么删除指针的操作将导致不确定后果。
template<class T>
inline void checked_delete(T * x)
{
//用于类型完整度的检查
typedef char type_must_be_complete[sizeof(T) ? 1 : -1];
(void) sizeof(type_must_be_complete);
delete x;
}
template<class T>
inline void checked_array_delete(T * x)
{
typedef char type_must_be_complete[sizeof(T) ? 1 : -1];
(void) sizeof(type_must_be_complete);
delete[] x;
}
3、scoped_ptr
unique_ptr其实就是scoped_ptr,scoped_ptr先出现在boost库种,随后被c++11采用,改名为unique_ptr。