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

  

    

    

原文地址:https://www.cnblogs.com/Duikerdd/p/12030949.html

时间: 2024-07-28 17:33:41

RAII思想之智能指针的相关文章

C++ 资源管理(RAII)--智能指针

1. 智能指针(Smart Pointer) i. 是存储指向动态分配(堆)对象指针的类 ii. 在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象 iii. RAII类模拟智能指针,见备注 2. C++11提供了以下几种智能指针,位于头文件<memory>,它们都是模板类 i. std::auto_ptr(复制/赋值) ii. std::unique_ptr  c++11 iii.std::shared_ptr  c++11 iv.std::weak_ptr    c++11

C++智能指针简单剖析

导读 最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等--.而且在看开源的C++项目时,也能随处看到智能指针的影子.这说明智能指针不仅是面试官爱问的题材,更是非常有实用价值. 下面是我在看智能指针时所做的笔记,希望能够解决你对智能指针的一些困扰. 目录

智能指针

RAII(Resource Acquisition Is Initialization) 资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放. ------------------------------------------------------------------------------------------- 为什么要有智能指针呢? 智能指针的存在是为了防止我们在大型项目中忘记释放空间而导

Boost智能指针-基础篇

简介 内存管理一直是 C++ 一个比较繁琐的问题,而智能指针却可以很好的解决这个问题,在初始化时就已经预定了删除,排解了后顾之忧.1998年修订的第一版C++标准只提供了一种智能指针:std::auto_ptr(现以废弃),它基本上就像是个普通的指针:通过地址来访问一个动态分配的对象.std::auto_ptr之所以被看作是智能指针,是因为它会在析构的时候调用delete操作符来自动释放所包含的对象.当然这要求在初始化的时候,传给它一个由new操作符返回的对象的地址.既然std::auto_pt

【转】C++智能指针简单剖析

原文链接:http://www.cnblogs.com/lanxuezaipiao/p/4132096.html 导读 最近在补看 <C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的 问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等…….而且在看开源的C++项目 时,也能随处看到智能指针的影子.这说明智能指针不仅

Boost智能指针-基础知识

简单介绍 内存管理一直是 C++ 一个比較繁琐的问题,而智能指针却能够非常好的解决问题,在初始化时就已经预定了删除.排解了后顾之忧.1998年修订的第一版C++标准仅仅提供了一种智能指针:std::auto_ptr(现以废弃),它基本上就像是个普通的指针:通过地址来訪问一个动态分配的对象. std::auto_ptr之所以被看作是智能指针.是由于它会在析构的时候调用delete操作符来自己主动释放所包括的对象. 当然这要求在初始化的时候,传给它一个由new操作符返回的对象的地址.既然std::a

C++智能指针简单解析

导读 最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等--.而且在看开源的C++项目时,也能随处看到智能指针的影子.这说明智能指针不仅是面试官爱问的题材,更是非常有实用价值. 下面是我在看智能指针时所做的笔记,希望能够解决你对智能指针的一些困扰. 目录

C/C++ 智能指针简单剖析

导读 最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等…….而且在看开源的C++项目时,也能随处看到智能指针的影子.这说明智能指针不仅是面试官爱问的题材,更是非常有实用价值. 下面是我在看智能指针时所做的笔记,希望能够解决你对智能指针的一些困扰. 目录

C++之智能指针

导读 一直对智能指针有一种神秘的赶脚,虽然平时没怎么用上智能指针,也就看过STL中的其中一种智能指针auto_ptr,但是一直好奇智能指针的设计因此,今天看了一下<C++ Primer Plus>中对智能指针的介绍,在此也总结一下. 目录 智能指针背后的设计思想 C++智能指针简单介绍 为什么摒弃auto_ptr? unique_ptr为何优于auto_ptr? 如何选择智能指针? 正文 1. 智能指针背后的设计思想 首先,我们来看一下普通对象与指针的区别: 为什么局部变量的对象不用手动释放资