• EC读书笔记系列之8:条款13、14、15


    条款13 以对象管理资源

    记住:

    ★为防止资源泄漏,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放

    ★两个常被使用的RAII classes分别是tr1::shared_ptr和auto_ptr。前者通常是较佳选择,∵其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null

    ------------------------------------------------------------------------

    为确保资源总是被释放,应将资源放进对象内,当控制流离开后,该对象的dtor会自动释放那些资源。以对象管理资源的两个关键想法:

      (1)获得资源后立刻放进管理对象;

      (2)管理对象运用dtor确保资源被释放

    auto_ptr特殊性质:通过copying函数复制它们,它们会变成NULL,而复制所得的指针将取得资源的唯一拥有权:

        std::auto_ptr<Investment> pInv1( createInvestment() );

        std::auto_ptr<Investment> pInv2( pInv1 );  //现pInv2指向对象,而pInv1为null!!!

    可见受auto_ptr管理的资源必须绝对没有一个以上的auto_ptr同时指向它。

    auto_ptr的一个替代方案是“引用计数型智能指针”,如tr1::shared_ptr。即该智能指针持续追踪共有多少个对象指向某笔资源,并在无人指向它时自动删除该资源。∴其copying行为正常很多:

        std::tr1::shared_ptr<Investment> pInv2( pInv1 ); //pInv1和pInv2指向同一个对象

    auto_ptr和shared_ptr两者都在其destructor内做delete而不是delete[]。这意味着在动态分配而得的array身上使用以上两者十分不好。尽管如此,其仍然能通过编译:

        std::auto_ptr<std::string> aps( new std::string[10] );

    条款14 在资源管理类(自己建立的)中小心copying行为

    记住:

    ★复制RAII对象必须一并复制它所管理的资源,∴资源的copying行为决定RAII对象的copying行为

    ★普遍而常见的RAII class copying行为是:抑制copying、引用计数法。不过其他行为也都可能被实现

    --------------------------------------------------------------------------------

    举例:

      为确保不忘记将一个被锁住的Mutex解锁,可建立一个class来管理机锁。这样的class基本结构由RAII守则支配:资源在构造期间获得,在析构期间释放

     1 class Lock {
     2     public:
     3         explicit Lock( Mutex *pm ) : mutexPtr( pm ) {
     4             lock( mutexPtr );     //获得资源
     5         }
     6 
     7         ~Lock() {
     8             unlock( mutexPtr );     //释放资源
     9         }
    10     private:
    11         Mutex *mutexPtr;            //raw资源
    12 };

    客户使用时:

    1 Mutex m;
    2 ...
    3 {                    //建立一个区块来定义critical section
    4     Lock ml( &m );    //锁定互斥器
    5     ...                //执行critical section内的操作
    6 }                    //区块最末尾自动解除互斥器锁定

    但若Lock对象被复制,会发生何事???

    当一个RAII对象被复制,会有两种处理方式:

    方式一:禁止复制

        将copying操作声明为private,所以对Lock而言看起来如下:

        class Lock : private Uncopyable {

        ...

        };

    方式二:对底层资源祭出“引用计数法”

        希望保有资源直到它的最后一个使用者(某对象)被销毁。复制RAII对象时应将该资源的被引用数递增。tr1::shared_ptr便是如此。

    对方法二,通常RAII类中只要内含一个tr1::shared_ptr即可实现reference-counting copying行为。而此处要定制tr1::shared_ptr的“删除器”,当引用次数为0时调用(∵tr1::shared_ptr的默认行为是当引用次数为0时删除其所指物):

     1 class Lock {
     2     public:
     3         explicit Lock( Mutex *pm ) :  mutexPtr( pm, unlock ) {
     4             
     5             lock( mutexPtr.get() ); //get函数返回sp内部raw指针(的复件
     6         }
     7 
     8     private:
     9         std::tr1::shared_ptr<Mutex> mutexPtr; //用智能指针替换raw pointer
    10 };

    此处无需dtor!因为编译器生成的dtor会自动调用其non-static成员(本例的mutexPtr)的dtor,而mutexPtr的dtor会在互斥器的引用次数为0时自动调用tr1::shared_ptr的删除器(本例为unlock)。

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

    记住:

    ★APIs往往要求访问原始资源,∴每个RAII class应提供一“取得其所管理之资源”的办法

    ★对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换较安全,但隐式转换对客户较方便。

    ---------------------------------------------------------------------------------

    有两方法可以将RAII class对象转换为其所内含之原始资源:显式隐式转换

    举例:

    1 class Font {
    2 
    3     public:
    4         explicit Font( FontHandle fh ):f( fh ) {}     //获得资源
    5         ~Font() { releaseFont(f); }                //释放资源
    6     private:
    7         FontHandle f;                          //原始字体资源
    8 };

    假设有大量与字体相关的C API处理的是FontHandles,那么就需将Font对象转换为FontHandle。Font class有两种做法:

    方法一:提供显式转换函数:

    1 class Font {
    2 
    3     public:
    4         ...
    5         FontHandle get() const { return f; } //显式转换函数
    6         ...
    7 };

    客户使用时:

    1 void changeFontSize( FontHandle f, int newSize ); //C API,需要原始资源
    2 Font f( getFont() ); //获取字体资源,Font是资源管理类
    3 int newFontSize;
    4 ...
    5 changeFontSize( f.get(), newFontSize ); //明白地将Font转换为FontHandle

    方法二:提供隐式转换函数:

    1 class Font {
    2 
    3     public:
    4         ...
    5         operator FontHandle() const { return f; } //隐式转换函数
    6         ...
    7 };

    客户使用时:

     1 changeFontSize( f, newFontSize ); //会将Font隐式转换为FontHandle 

    tr1::shared_ptr和auto_ptr都提供一个get成员函数,用来执行显式转换,其会返回智能指针内部的原始指针(的复件)。而且tr1::shared_ptr和auto_ptr也重载了指针取值操作符(operator->和operator*),它们允许隐式转换至底部原始指针:

    std::tr1::shared_ptr<Investment> pi1( createInvestment() );  //令tr1::shared_ptr管理一笔资源

    bool taxlabel = !( pi1->isTaxFree() );   //经由operator->访问资源

                          //或bool taxlabel  = !( (*pi1).isTaxFree() ); //经由operator*访问资源

    补充:

    RAII class内的那个返回原始资源的函数,确实是与“封装”思想矛盾。但RAII并非为了封装某物而存在,所以也没关系。就像很多设计良好的classes一样,它隐藏了客户不需要看的部分,但备妥客户需要的所有东西。

  • 相关阅读:
    unity free asset
    Unity3d Serialize问题
    野蛮能带来繁荣是怎么回事?
    如何给unity3d工程加入依赖的android工程
    unity3d 导入google play services插件工程
    NGUI中UILabel使用url标签的一个bug
    数据结构
    git命令
    面试算法经典问题
    Http Client 源码分析
  • 原文地址:https://www.cnblogs.com/hansonwang99/p/4935985.html
Copyright © 2020-2023  润新知