1.智能指针:智能指针就是智能的、自动化的管理指针所指向的动态资源的释放,并且可以如同指针一样使用。智能指针是RALL(初始化立即获取资源)思想的一种实现,其中初始化利用构造函数,之后将资源保存起来最后让析构函数自动清理。
2.引入智能指针原因:总的来说,是防止程序执行流的改变、或者人为因素造成的内存泄露问题,在此我们应该知道,影响执行流改变的常见语句有:goto,抛异常,return,break,continue等。
3.智能指针发展:
(1)最早期的auto_ptr
autoptr是一种有着严重缺陷的智能指针,缺陷在于当我们进行拷贝构造时,如果为浅拷贝时,可能会出现同一块空间释放多次的问题,如果为深拷贝也不合理,因为
指针的拷贝,本身就希望指向同一块空间;因此,在拷贝构造这里就出现了大的问题。
即再解决对象赋值、拷贝构造的时候,如a=b,先将a的地址空间释放,然后将b.ptr指针赋值给a.ptr,最后将地址空间的管理权交给a.ptr,并且将 b.ptr置为NULL.所以,在此操作
之后,原来的指针将不能再使用,因此其缺点在于,一个智能指针只能指向一块内存,不能有几个指针指向同一块内存空间。
鉴于autoptr的严重缺陷,开发者又自主实现解决以上弊端的智能指针:
(2)scoped_ptr
它所采用的解决方案就是防函数,对拷贝构造函数、赋值运算符的重载声明为私有的 或者保护的,但不去实现它,即实现了防止拷贝,防止赋值的功能
(3)shared_ptr
(4)weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,像旁观者那样观测资源的使用情况但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
2.引入智能指针原因:总的来说,是防止程序执行流的改变、或者人为因素造成的内存泄露问题,在此我们应该知道,影响执行流改变的常见语句有:goto,抛异常,return,break,continue等。
3.智能指针发展:
(1)最早期的auto_ptr
autoptr是一种有着严重缺陷的智能指针,缺陷在于当我们进行拷贝构造时,如果为浅拷贝时,可能会出现同一块空间释放多次的问题,如果为深拷贝也不合理,因为
指针的拷贝,本身就希望指向同一块空间;因此,在拷贝构造这里就出现了大的问题。
关于auto_ptr的模拟实现:
template <class T>
class AutoPtr
{
private:
T* _ptr;
public:
AutoPtr(T* ptr)//构造函数
:_ptr(ptr)
{}
~AutoPtr()//析构函数
{
cout<<"~AutoPtr()//析构函数"<<endl;
if(_ptr)
{
delete _ptr;
}
}
//1.管理权的转移
//问题缺陷:当一个智能指针交出对空间的管理权之后,将它置空,不能再对其解引用赋值,废掉
AutoPtr(AutoPtr<T>& ap)//拷贝构造函数
{
_ptr=ap._ptr;
ap._ptr=NULL;//退出管理
}
AutoPtr<T>& operator=(AutoPtr<T>& ap)//赋值操作符的重载
{
if(this!=&ap)
{
if(_ptr)
{
delete _ptr;
}
_ptr=ap._ptr;
ap._ptr=NULL;//退出管理
}
return *this;
}
T& operator*()//重载*
{
return *_ptr;
}
T* operator->()//重载->
{
return _ptr;
}
};
解决方案:管理权转移
即再解决对象赋值、拷贝构造的时候,如a=b,先将a的地址空间释放,然后将b.ptr指针赋值给a.ptr,最后将地址空间的管理权交给a.ptr,并且将 b.ptr置为NULL.所以,在此操作
之后,原来的指针将不能再使用,因此其缺点在于,一个智能指针只能指向一块内存,不能有几个指针指向同一块内存空间。
鉴于autoptr的严重缺陷,开发者又自主实现解决以上弊端的智能指针:
(2)scoped_ptr
它所采用的解决方案就是防函数,对拷贝构造函数、赋值运算符的重载声明为私有的 或者保护的,但不去实现它,即实现了防止拷贝,防止赋值的功能
关于scoped_ptr的模拟实现:
template <class T>
class ScopedPtr
{
protected:
T* _ptr;
public:
ScopedPtr(T* ptr)//构造函数
:_ptr(ptr)
{}
~ScopedPtr()//析构函数
{
if(_ptr)
{
delete _ptr;
}
}
T& operator*()//*的重载
{
return *_ptr;
}
T* operator->()//重载->
{
return _ptr;
}
//防拷贝的实现,即将拷贝构造、赋值运算符重载函数声明成私有的,并且不定义
private:
ScopedPtr(const ScopedPtr<T>& sp);
ScopedPtr<T>& operator=(const ScopedPtr<T>& sp);
};
虽然 表面上解决了auto_ptr的缺陷,但是没有了拷贝、赋值功能,为了不仅可以 屏蔽auot_ptr缺陷,还能拷贝、赋值,我们又提出来了一下:
(3)shared_ptr
它所采用的技术是引用计数思想,在对象构造时记下使用次数,到最后只有一个引用计数的时候再释放,所以依然支持拷贝构造和赋值的操作
关于shared_ptr的模拟实现:
template <class T>
class SharedPtr
{
protected:
T* _ptr;
int* _RefCount;
public:
SharedPtr(T* ptr)//构造函数
:_ptr(ptr)
,_RefCount(new int(1))
{}
~SharedPtr()//析构函数
{
if(--(*_RefCount)==0)
{
delete _ptr;
delete _RefCount;
}
}
SharedPtr(SharedPtr<T>& sp)//拷贝构造函数
{
_ptr=sp._ptr;
_RefCount=sp._RefCount;
++(*_RefCount);//拷贝构造之后,this多了一个对象指向自己的空间
}
SharedPtr<T>& operator=(SharedPtr<T>& sp)//赋值运算符的重载
{
if(_ptr!=sp._ptr)
{
if(--(*_RefCount)==0)
{
delete _ptr;//转变指向之前,先检查自己原来在的那块空间是否只有自己一个人管理
delete_RefCount;
}
_ptr=sp._ptr;
++(*_RefCount);
}
return *this;
}
T& operator*()//*的重载
{
return *_ptr;
}
T* operator->()//重载->
{
return _ptr;
}
};
shared_ptr可以说是比较完美的智能指针了,解决了它之前几个智能指针所存在的问题但是,它的缺陷却在于,它存在着循环引用的问题:
关于循环引用:在使用shared_ptr时,由于节点之间的相互引用使得多个结点指向同一块空间,引用计数不为1,从而在释放空间时,p1等p2释放,p2等p1释放,从而引起的循环引用问题,造成内存泄露。
(4)weak_ptr
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,像旁观者那样观测资源的使用情况但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
引入weak_ptr弱引用指针即可解决循环引用问题。weak_ptr不会修改引用计数。
关于weak_pt的模拟实现:
template<class T>
class WeakPtr
{
WeakPtr()
:_ptr(NULL)
{}
WeakPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
{}
T& operator*()//*的重载
{
return *_ptr;
}
T* operator->()//重载->
{
return _ptr;
}
private:
T* _ptr;
};