智能指针

    xiaoxiao2023-11-08  163

    RAII

    利用对象生命周期控制程序资源的简单技术,在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期始终保持有效,在析构时释放资源,就是把管理资源的责任托管给了对象

    优点

    不需要显式释放资源对象的资源在生命周期内始终有效

    智能指针的原理

    RAII特性重载operator*和operator->,具有像指针一样的行为

    auto_ptr

    template < class T> class Auto_Ptr{ public: Auto_Ptr(T* ptr = nullptr) :_ptr(ptr) {} ~Auto_Ptr(){ if (_ptr) delete _ptr; } //发生拷贝时将ap与资源断开联系,防止一块空间被多个对象使用 //但是拷贝后的ap指针对象赋空,后续访问ap对象会出问题 Auto_Ptr(Auto_Ptr<T>& ap) :_ptr(ap._ptr) { ap._ptr = nullptr; } Auto_Ptr<T>& operator= (Auto_Ptr<T>& ap){ if (this != &ap){ if (_ptr)//释放当前对象资源 delete _ptr; //转移ap的资源 _ptr = ap._ptr; ap._ptr = nullptr; } return *this; } T& operator*(){ return *_ptr; } T* operator->(){ return _ptr; } private: T* _ptr; };

    unique_ptr

    auto_ptr拷贝构造后原对象赋空,所以C++11提供了unique_ptr,实现原理就是防拷贝

    template <class T> class Unique_Ptr{ public: Unique_Ptr(T* ptr = nullptr) :_ptr(ptr) {} ~Unique_Ptr(){ if (_ptr) delete _ptr; } T& operator*(){ return *_ptr; } T* operator->(){ return _ptr; } private: Unique_Ptr(Unique_Ptr<T> const&); Unique_Ptr& operator=(Unique_Ptr<T> const&); //C++11写法 //Unique_Ptr(Unique_Ptr<T> const&) = delete; //Unique_Ptr& operator=(Unique_Ptr<T> const&) = delete; T* _ptr; };

    shared_ptr

    C++11提供了支持拷贝的shared_ptr,实现原理是通过引用计数的方式实现多个shared_ptr对象之间共享资源shared_ptr内部维护了一份计数,用来记录该份资源被几个对象共享在对象调用析构函数时,计数减一,当计数为0时,说明自己是最后一个资源,必须释放该资源,如果不是0,说明有其他对象还在使用该资源,不能释放 template <class T> class Shared_Ptr{ public: Shared_Ptr(T* ptr = nullptr) :_ptr(ptr) , _pRefCount(new int(1)) , _pMutex(new mutex) { //如果是空指针对象,引用计数给0 if (_ptr == nullptr) *_pRefCount = 0; } ~Shared_Ptr(){ Release(); } Shared_Ptr(Shared_Ptr<T> const& sp) :_ptr(sp._ptr) , _pRefCount(sp._pRefCount) , _pMutex(sp._pMutex) { //拷贝一非空指针对象增加引用计数 if (_ptr) AddRefCount(); } Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp){ if (_ptr != sp._ptr){ Release(); _ptr = sp._ptr; _pRefCount = sp._pRefCount; _pMutex = sp._pMutex; if (_ptr) AddRefCount(); } return *this; } T& operator*(){ return *_ptr; } T* operator->(){ return _ptr; } int AddRefCount(){ //引用计数加一的原子操作 _pMutex->lock(); ++(*_pRefCount); _pMutex->unlock(); return *_pRefCount; } int SubRefCount(){ //引用计数减一的原子操作 _pMutex->lock(); --(*_pRefCount); _pMutex->unlock(); return *_pRefCount; } private: void Release(){ if (_ptr&&SubRefCount() == 0){ //引用计数为0时释放资源 delete _ptr; delete _pRefCount; } } T* _ptr; int* _pRefCount;//引用计数 mutex* _pMutex;//互斥锁 };

    线程安全

    智能指针的引用计数时线程安全的智能指针管理的对象存放在堆上,两个线程同时去访问会导致线程安全问题 shared_ptr<int> X(new int);//线程间共享的shared_ptr shared_ptr<int> A; //线程A的局部变量 shared_ptr<int> B(new int);//线程B的局部变量

    创建好线程

    先执行A = X,指针对象赋值过去,但引用计数还没来得及赋值就切换到了另一个线程

    执行X = B,先是指针赋值过去

    再把引用计数赋值过去

    此时int1对象已销毁,x._ptr成了空悬指针,最后回到线程A

    所以多线程读写shared_ptr时需要加锁

    循环引用

    struct ListNode{ int _data; //shared_ptr<ListNode> _prev; //shared_ptr<ListNode> _next; weak_ptr<ListNode> _prev; weak_ptr<ListNode> _next; ~ListNode(){ cout << "~ListNode" << endl; } };

    分析

    当node1的_next指向node2,node2的_prev指向node1时当node1和node2析构引用计数减到1,但是_prev和_next还指向节点当_prev和_next析构,node1和node2也就释放了但是node1和node2分别由_prev和_next管理,_prev和_next属于node的成员,谁也不会释放

    weak_ptr

    为解决shared_ptr的循环引用的问题C++11标准库引入入weak_ptrweak_ptr不是智能指针,只能配合shared_ptr使用weak_ptr不会改变循环技术,析构函数不释放资源

    删除器

    如果对象时静态建立的,shared_ptr设计了一个仿函数删除器来解决

    template <class T> struct FreeFunc{ void operator()(T* ptr){ free(ptr); } }; template <class T> struct DeleteFunc(){ void operator()(T* ptr){ delete[] ptr; } };

    最新回复(0)