智能指针浅析

来源:互联网 发布:小米刷机后数据恢复 编辑:程序博客网 时间:2024/05/29 17:18

                                 智能指针

一、什么是智能指针?

  资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

  对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。

二、为什么要有智能指针的存在?

  由于C++语言没有内存的自动回收机制,程序员每次new出来的内存都要手动delete释放,而当程序员忘delete,或者流程复杂,异常导致没有运行到delete提前退出,就会造成内存泄漏,防不胜防,则用智能指针可以有效缓解这些问题。

三、智能指针的发展

         1.Auto_ptr 的管理权转移(了解机制,不建议使用)

用一段代码配合解析

template<class T>class AutoPtr{public:AutoPtr(T* ptr):_ptr(ptr){}~AutoPtr(){if (_ptr){printf("delete:0x%p\n", _ptr);delete _ptr;}}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}// ap2(ap1)AutoPtr(AutoPtr<T>& ap):_ptr(ap._ptr){ap._ptr = NULL;}// ap2 = ap3AutoPtr<T>& operator=(AutoPtr<T>& ap){if (this != &ap){if (_ptr){printf("delete:0x%p\n", _ptr);delete _ptr;}_ptr = ap._ptr;ap._ptr = NULL;}return *this;}protected:T* _ptr;};void TestAutoPtr(){int* p = new int(10);AutoPtr<int> ap1(p);AutoPtr<int> ap2(ap1);AutoPtr<int> ap3(ap2);AutoPtr<int> ap4(new int(20));ap3 = ap4;}

管理权转移的方法会导致有些对象悬空,解引用它们就会使程序崩溃。例如上面代码中

这块空间的管理权从ap1转给ap2,ap1则指向空,因此不能再使用,同理代码中的p,ap2,ap4都不能再使用。

   2.Boost库的智能指针(ps:新的C++11标准中已经引入了unique_ptr/shared_ptr/weak_ptr)


(1)scoped_ptr/scoped_array 防拷贝(只声明不定义,声明成私有)

模拟实现scoped_ptr

template<class T>class ScopedPtr{public:// RAIIScopedPtr(T* ptr):_ptr(ptr){}~ScopedPtr(){printf("delete:0x%p\n", _ptr);delete _ptr;}// 像指针一样--智能指针的特性T& operator*(){return *_ptr;}T* operator->(){return _ptr}// 防拷贝// 1.只声明不实现// 2.声明成私有保护private:ScopedPtr(const ScopedPtr<T>&);ScopedPtr<T>& operator=(const ScopedPtr<T>&);protected:T* _ptr;};void TestScopedPtr(){ScopedPtr<int> sp1(new int(10));ScopedPtr<int> sp2(sp1);}

ScopedPtr对成员函数的拷贝构造和赋值运算符重载进行了保护,直接不能拷贝了,其他成员函数和AutoPtr一样。

模拟实现scoped_array

template<class T>class ScopedArray{public:ScopedArray(T* ptr):_ptr(ptr){}~ScopedArray(){if (_ptr){printf("delete[] 0x%p\n", _ptr);delete[] _ptr;}}T& operator[](size_t pos){return _ptr[pos];}protected:ScopedArray(const ScopedArray<T>& s);ScopedArray<T>& operator=(const ScopedArray<T>& s);protected:T* _ptr;};

ScopedArray与ScopedPtr相比只重载了operate[]

(2)shared_ptr/shared_array  采用引用计数

模拟实现 shared_ptr

template<class T, class D = Deleter>class SharedPtr{public:SharedPtr(T* ptr = NULL, D del = Deleter()):_ptr(ptr),_refCount(new int(1)),_del(del){}void Release(){if (--(*_refCount) == 0){if (_ptr){printf("delete: 0x%p\n", _ptr);//delete _ptr;_del(_ptr);}delete _refCount;}}~SharedPtr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}// sp2(sp1)SharedPtr(const SharedPtr<T, D>& sp):_ptr(sp._ptr),_refCount(sp._refCount){++(*_refCount);}// sp2 = sp3SharedPtr<T, D>& operator=(const SharedPtr<T, D>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_refCount = sp._refCount;++(*_refCount);}return *this;}protected:T* _ptr;int* _refCount;D _del;};

模拟实现 shared_array

template<class T>class SharedArray{public:SharedArray(T* ptr = NULL):_ptr(ptr),_refCount(new int(1)){}void Release(){if (--(*_refCount) == 0){if (_ptr){printf("delete: 0x%p\n", _ptr);delete[] _ptr;}delete _refCount;}}int RefCount(){return *_refCount;}T& operator[](size_t pos){return _ptr[pos]}~SharedArray(){Release();}// sp2(sp1)SharedArray(const SharedArray<T>& sp):_ptr(sp._ptr),_refCount(sp._refCount){++(*_refCount);}// sp2 = sp3SharedArray<T>& operator=(const SharedArray<T>& sp){if (_ptr != sp._ptr){Release();_ptr = sp._ptr;_refCount = sp._refCount;++(*_refCount);}return *this;}protected:T* _ptr;int* _refCount;};//struct AA//{//~AA()//{//cout<<"~AA()"<<endl;//}//};struct DelArray{template<class T>void operator()(T* ptr){delete[] ptr;}};
 *对于shared_array不小心就会引发new/delete与new[]/delete[]的匹配问题,不匹配就可能会出现问题:

【1】.内置类型:不匹配不出现问题,但是最好匹配使用;

【2】.自定义类型:当自定义类型有自己的析构函数时,编译器会在开辟的空间前多开四个字节,这时不匹配使用,释放时就少释放了四个字节,造成内存泄漏;而自己没有定义析构函数时,编译器会优化不多开四个字节,不匹配不会出现问题。

(3)weak_ptr  解决循环引用问题

什么是循环引用?

第一块空间被下一块空间的_prev管理,而下一块空间被前一块空间的_next管理,构成循环,使得空间无法释放,用weak_ptr同shared_ptr构成友元可以解决。
模拟实现

template<class T>class WeakPtr{public:WeakPtr():_ptr(NULL){}WeakPtr(const SharedPtr<T>& sp):_ptr(sp._ptr){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;};
struct ListNode{WeakPtr<ListNode> _prev;WeakPtr<ListNode> _next;/*SharedPtr<ListNode>_next;        ListNode>_prev;*/};void TestCycleRef(){// 循环引用SharedPtr<ListNode> cur = new ListNode;SharedPtr<ListNode> next = new ListNode;cur->_next = next;next->_prev = cur;}