继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

以对象管理资源

慕标5832272
关注TA
已关注
手记 1229
粉丝 229
获赞 1001

以对象管理资源

checklists

  • 函数直接返回 raw 指针极易造成资源泄漏,违背了“让接口容易被正确使用,不易被误用”的原则,替代可以选择返回RAII对象如 指针指针;

  • RAII是最常用的资源管理类对象,调用 delete 销毁指向的对象,对象的析构函数中不能抛出异常。

why

  • 常见的计算机资源包含:动态分配的内存、文件描述器、互斥锁、图形界面中的字型和画刷、数据库连接及网络 sockets 等,使用后必须还给系统。

常规方式


class Investment {...}; // 投资类基类Investment* createInvestment( );  // 工厂方法,返回函数内动态创建的对象的指针,调用者需保证释放void f(){
    Investment* pInv = createInvestment();    
    do{        if( nullptr == pInv ) {            log.error( "..." );            break;
        }        
        if( ERROR == dosomething( ) ){            log.error( "..." );            break;        
        }
        ...
    }while(0) // for resource pInv
    
    if( pInv ) delete pInv;    return ;
}

借助于 do_while_0 实现类似于 go_to 的语法效果,已经极大改善了异常分支时资源的释放的代码结构,并且当上述结构遇到多个资源,会导致 do_while_0 的更加复杂的嵌套,灵活性很差。

对象管理资源的方式

shared_ptr

void f(){        std::shared_ptr<Investment> pInv( createInvestment() );        // 利用 shared_ptr 对象管理资源,脱离作用域,自动调用 delete pInv
        assert( nullptr != pInv.get() );        
        if( ERROR == dosomething( ) ){            log.error( "..." );            return;        
        }        
        return;
}
  • 获取资源后立刻放进管理对象,如上的 shared_ptr 对象,又被称为 Resource Acquisition Is Initialization; RAII;

  • 管理对象利用析构函数来确保资源被释放,但是注意析构函数不能抛出异常,需要仔细处理资源的释放过程。

  • shared_ptr 释放资源时,调用 delete 函数而非 delete[]。

自定义资源管理对象

class Lock{public:  explicit Lock( Mutex* pm)
  : mutexPtr( pm )
  {
      lock( mutexPtr );
  }
  ~Lock()
  {
      unlock( mutexPtr );
  }private:
  Mutex* mutexPtr;
};// usage exampleMutex m;
...
{    Lock m1(&m);    Lock m2( m1 ); // ERROR, Lock 对象应禁止复制}

自定义管理对象的复制策略一般分为四种:

  • 禁止复制策略

  • 对底层资源采用引用计数,保有资源,直到最后一个使用者被销毁,此时才释放资源,复制时,引用计数增加。

  • 复制底层资源,即对对象实行深拷贝;

  • 转移底部对象的拥有权,如 move_construct 函数。

禁止复制策略

class Uncopyable{
    protected:
    Uncopyable(){} // 1, 必须实现阻止系统默认构造函数;
                   // 2, 外部不可见
                   // 3, 子类中可见
    ~Uncopyable() {}    private() {}
    Uncopyable( const Uncopyable&); // 1, 只有声明
                                    // 2, 继承后称为子类的 private 成员函数,阻止自动生成的复制构造函数
    Uncopyable& operator=( const Uncopyable& ); // 阻止默认拷贝赋值函数 };class Lock : private Uncopyable // 1, private 不改变可见性{
    ...
}

shared_ptr 指定释放方式

public:explicit Loak( Mutex* pm ) : mutexPtr( pm, unlock) { // 指定 unlock 为 shared_ptr 销毁时调用的资源释放方式
    lock( pm );
}private:std::shared_ptr<Mutex> mutexPtr;

资源管理类需要提供对原始资源的访问

应尽可能的通过资源管理类操作资源,但是为了保证兼容老式 C 风格的接口, 如使用需要使用西永调用等API时,需要访问原始资源。

两种访问方式

1, 显示转换,如 shared_ptr<T>.get()
2, 隐式转换函数, 重载转换运算符 operator T()

new 和 delete 必须对应

new 对应 delete, new [] 对应 delete[]
避免对数组 typedef , 防止 delete 与 new[] 不匹配问题
数组对象内存前有一个表示数组的个数, 调用 delete[] 会先读出数组的个数,然后对应的调用 n 次对象的析构,delete 直接释放第一个元素。

以独立语句初始化智能指针

processWidget( std::shared_ptr<Widgt>( new Widgt), priority() )// C++ 不能保证上述参数核算的顺序,比如有如下两种:// 1,  调用 priority() // priority 异常不会导致资源泄露//     new Widgt() //     构造 shared_ptr// 2,  new Widgt()//     调用 priority()  // proority 异常会导致资源泄露//     构造 shared_ptrshared_ptr<Widgt> pw( new Widgt);
processWidgt( pw, priority );



作者:呆呆的张先生
链接:https://www.jianshu.com/p/0e6db11a7fc4


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP