zoukankan      html  css  js  c++  java
  • RAII思想之智能指针

        RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种利用对象生命周期来控制资源的技术。

        简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源

        这种技术是C++为了控制资源,避免内存泄漏的惯用法。

    // 使用RAII思想设计的SmartPtr类
    template<class T>
    class SmartPtr {
        public:
            SmartPtr(T* ptr = nullptr)
            : _ptr(ptr)
            {}
            ~SmartPtr()
            {
            if(_ptr)
            delete _ptr;
        }
        private:
            T* _ptr;
    };

        因为C++并没有垃圾回收机制,要是忘记回收垃圾,就会造成内存泄漏。所以,RAII思想的提出,便解决了这个问题。

        RAII思想设计实例:

        智能指针: 

             起初在C++标准库里面是没有智能指针的,最早的智能指针在Boost库里面,直到C++11才加入了shared_ptr和unique_ptr以及weak_ptr。

        特点: 1.RAII思想 2.具有指针行为

        版本1: std:::auto_ptr  

    //RAII + *和->的重载
    template<class T>
    class SmartPoint{
        public:
             SmartPoint(T* ptr=nullptr):_ptr(ptr)
             {}
             ~SmartPoint()
             { 
                std::cout<<"delete"<<_ptr<<std::endl;
                delete _ptr;
            }
            
            T& operator*(){return *_ptr};
            T* operator->(){return _ptr};
            
            private:
                T* _ptr;
                
    };  

        缺陷: 管理权转移会导致原对象指针置空,无法正常使用

        详情如下,关于auto_ptr拷贝构造和赋值重载的实现:

    template<class T>
    class AutoPtr()
    {
                  //p对象资源转移后,自身置空,与资源断开联系
                  AutoPtr(AutoPtr<T>& p):_ptr(p._ptr)
                 {
                       p._ptr = NULL;      
                 }
                  AutoPtr<T>& operator=(AutoPtr<T>& p)
                 {
                       //检测是否是给自己赋值
                       if(this != &p)
                       {
                            //释放当前资源
                            if(_ptr)
                                 delete _ptr;
    
                            //将p资源转移给本对象,p的指针置空
                             _ ptr = p._ptr;
                             p._ptr = NULL;
                       }
                 }
                 private:
               T* _ptr; };

        版本2: std::unique_ptr   ---  防拷贝

    template<T>
    class UniPtr()
    {
            //简单粗暴防拷贝
            //C++11防拷贝: delete
             UniPtr(UniPtr<T> const&) = delete;
             UniPtr<T>& operator=(Unique<T> const&) = delete;
         private:
             T* _ptr;      
    };    
    

        版本3: std::shared_ptr  ---  引用计数 

        原理: 在拷贝构造时,使用同一份计数

                 (1)每个shared_ptr对象内,都有一份资源被多少个对象共享的计数

                            (2)对象销毁时,计数-1 

                  如果计数==0,没有其他对象共享,释放资源

                                                       如果计数>0,有其他对象共享,不释放资源 

    #include<thread>
    #include<mutex>
    template <class T>
    class SharedPtr
    {
        public:
            //构造
            SharedPtr(T* ptr = nullptr)
            : _ptr(ptr)
            , _pRefCount(new int(1))
            , _pMutex(new mutex)
            {
                // 如果是一个空指针对象,则引用计数给0
                if (_ptr == nullptr)
                    *_pRefCount = 0;
            }
            
            //析构
            ~SharedPtr() {Release();}
            
            //拷贝构造
            SharedPtr(const SharedPtr<T>& sp)
            : _ptr(sp._ptr)
            , _pRefCount(sp._pRefCount)
            , _pMutex(sp._pMutex)
            {
                // 如果是一个空指针对象,则不加引用计数,否则才加引用计数
                if (_ptr)
                    AddRefCount();
            }
            
            // sp1 = sp2
            SharedPtr<T>& operator=(const SharedPtr<T>& sp)
            {
                //if (this != &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 UseCount() {return *_pRefCount;}
            T* Get() { return _ptr; }
    
            //原子计数: ++操作    
            int AddRefCount()
            {
                // 加锁或者使用加1的原子操作
                _pMutex->lock();
                ++(*_pRefCount);
                _pMutex->unlock();
                return *_pRefCount;
            }
            //原子计数: --操作
            int SubRefCount()
            {
                // 加锁或者使用减1的原子操作
                _pMutex->lock();
                --(*_pRefCount);
                _pMutex->unlock();
                return *_pRefCount;
            }
            
        private:
            //--与判断释放操作
            void Release()
            {
                // 引用计数减1,如果减到0,则释放资源
                if (_ptr && SubRefCount() == 0)
                {
                    delete _ptr;
                    delete _pRefCount;
                }
            }
            
        private:
            int* _pRefCount; // 引用计数,用指针让所有对象共享该值
            T* _ptr; // 指向管理资源的指针
            mutex* _pMutex; // 互斥锁
     };
    

         关于shared_ptr的两个问题:

        线程安全问题:  不是100%安全

        场景: 两个线程同时读写同一个shared_ptr

        1.引用是安全且无锁的

        2.对象读写不安全,因为有两个数据成员,不能原子化操作                改进:代码上加锁控制

          

          实例:

                              

        循环引用问题:

        场景: 两个对象中的shared_ptr互相指向对方,导致两者计数变为2,pa,pb销毁之后,资源计数变为1,此时,即A内部有指向B,B内部有指向A,这样对于A,B必定是在A析构后B才析构,对于B,A必定是在B析构后才析构A,这就是循环引用问题,违反常规,导致内存泄露。

              

              

                解决: 使用weak_ptr替代相互引用的shared_ptr

        weak_ptr: weak_ptr是弱共享指针,其实就是share_ptr的辅助指针,不具备指针的功能。主要是为了协助 shared_ptr 工作,可以观测资源的使用情况。weak_ptr 只对 shared_ptr 进行引用,不会改变引用计数,当被观察的 shared_ptr 失效后,相应的 weak_ptr 也相应失效。

       主要功能: 1.不会改变引用计数,可以观测资源使用情况,生命周期和shared_ptr一样

             2.可以使用lock来借用shared_ptr来完成操作

       

       非new对象问题:

       如果管理的是new出来的多个对象,怎么删除对象呢,shared_ptr提供了删除器

    // 仿函数的删除器 --- 在创建智能指针的时候,传参释放方式
    template<class T>
    struct FreeFunc {
        void operator()(T* ptr)
        {
        cout << "free:" << ptr << endl;
        free(ptr);
        }
    };
    
    template<class T>
    struct DeleteArrayFunc {
        void operator()(T* ptr)
        {
        cout << "delete[]" << ptr << endl;
        delete[] ptr;
        }
    };
    
    int main()
    {
    FreeFunc<int> freeFunc;
    shared_ptr<int> sp1((int*)malloc(4), freeFunc); //第二个参数为删除器选择
    DeleteArrayFunc<int> deleteArrayFunc;
    shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc);
    return 0;
    }
    

      

        

        

        

  • 相关阅读:
    Web网页安全色谱
    控件继承
    加密(转摘)
    关于Chart控件X轴数据显示不全解决方法。
    orcle 创建表空间用户
    oracle REGEXP_REPLACE
    產生64位隨机無重復碼
    简单跨浏览器通信.
    [原創]加載動態JS文件.
    层的拖放
  • 原文地址:https://www.cnblogs.com/Duikerdd/p/12030949.html
Copyright © 2011-2022 走看看