C++中的智能指针

来源:互联网 发布:空间大师软件 编辑:程序博客网 时间:2024/06/05 04:51
1.智能指针:智能指针就是智能的、自动化的管理指针所指向的动态资源的释放,并且可以如同指针一样使用。智能指针是RALL(初始化立即获取资源)思想的一种实现,其中初始化利用构造函数,之后将资源保存起来最后让析构函数自动清理。 

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;};