• Effective C++看书笔记(3):资源管理


    13:以对象管理资源

    void f()
    {
        A* a=createA();
        
        
        delete a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样写实际上很多情况都删除不了,比如提前退出了函数,抛出了异常,后续维护代码修改为提前return 。

    所以利用对象的析构来管理内存资源

    例子auto_ptr

    void f()
    {
        std::auto_ptr<A> ptr(createA());
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这就是RAII(Resource Acquisition Is Initialization)

    但是需要注意指针不要指向同一个对象。不然会删除多次。

    为了预防这个问题,auto_otr有一个不寻常的性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权。

    std::auto_ptr<A> ptr(createA());
    std::auto_ptr<A> ptr2(ptr);//ptr会被设置为Null
    std::auto_ptr<A> ptr=ptr2;//ptr2会被设置为null
    
    • 1
    • 2
    • 3

    这样的性质导致了一些局限性,有时候我们就是需要多个指针指向一个对象,然后只释放一次。

    替代方案就是shared_ptr

    void f()
    {
        std::tr1::shared_ptr<A> ptr(createA());
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是它打破不了环形引用。

    auto_ptr和shared_ptr(一般都用这个),两者都在其析构函数内做delete而不是delete[]动作,所以不要在动态分配得到的array身上使用auto_ptr或tr1::shared_ptr

    vector和boost::scoped_array和boost::shared_array都可以替代array。

    14:在资源管理类中小心copying行为

    如果资源不是在堆上,那么auto_ptr和tr1::shared_ptr就不适合了。

    这种情况就需要自己建立资源管理类

    例如

    针对Mutex的lock和unlock,建立资源管理类Lock

    class Lock{
    public:
        explicit Lock(Mutex* pm):mutenPtr(pm)
        {
            lock(mutexPtr);
        }
        
        ~Lock(){unlock(mutexPtr);}
        private:
        Mutex *mutexPtr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样调用来使用

    Lock m1(&mutex);
    
    • 1

    但是如果Lock被复制怎么办?

    Lock m1(&m);
    Lock m2(m1);
    
    • 1
    • 2

    大多数时候有两种选择

    • 禁止复制RAII对象,很多时候允许复制并不合理。即把copying操作声明为private
    • 对底层资源祭出引用计数法:有时候希望资源被最后一个使用者销毁。这种情况复制RAII对象,应该把底层资源引用计数加1。此时即可使用tr1::shared_ptr。
      • 但是shared_ptr默认行为是当引用次数为0是删除其所指物,而不是我们想要的unlock。
      • 但我们可以传入一个函数对象即可让引用计数为0是,它执行我们的行为。
    class Lock{
    public:
        explicit Lock(Mutex* pm):mutenPtr(pm,unlock)
        {
            lock(mutexPtr.get());
        }
        
        private:
        std::tr1::shared_ptr<Mutex> mutexPtr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其它方式相对不常见

    • 复制底部资源:深拷贝一份
    • 转移底部资源的拥有权:

    15:在资源管理类中提供对原始资源的访问

    把智能指针转化为真实资源(普通指针)可以调用shared_ptr和auto_ptr的get成员函数。

    也可以隐式(操作符->和*)这样来使用。

    但是显式转换更受欢迎,不容易出错。

    16:成对使用new和delete时要采取相同形式

    delete单一对象就是delete。

    delete对象数组需要delete []。两个都必须严格对应原来的形式。

    • 你写的class用一个指针指向动态分配的内存,就必须把所有对象构造出视乎,这样才能delelte析构所有的对象。
    • 最好尽量不要对数组形式做typedef动作。

    17:以独立语句将newed对象置入智能指针

    不要一句把new对象,智能指针构造函数放到一个地方去,而应该拆分。否则可能出现异常导致资源泄露

    processA(std::tr1::shared_ptr<A>(new A),priority());//这是错误的。
    
    //这是正确的
    std::tr1::shared_ptr<A> pw(new A)
        processA(pw,priority())
    
    • 1
    • 2
    • 3
    • 4
    • 5

    原因是执行顺序可能重排序。

    错误的这一句可能是

    • new A
    • priority
    • new A放入智能指针

    priority出现异常了,new A对象就丢失了。

  • 相关阅读:
    Spring boot发布到k8s并加载Configmap配置文件,实现配置热更新
    2022年python国际化课程上机考试三题解
    c# IEnumerable--扩展方法
    第三章 寄存器 (内存访问)
    ubuntu20.04部署gost服务
    【Linux】权限
    「优选算法刷题」:二进制求和
    【C++】 内存管理
    〔支付接入〕微信的 h5 支付和 jsapi 支付
    【算法|滑动窗口No.1】leetcode209. 长度最小的子数组
  • 原文地址:https://blog.csdn.net/weixin_45593271/article/details/133608454