• 面向对象编程系列六:句柄类


    (1)句柄类

    前面的章节中提到,在继承体系中,从派生类对象到基类对象的转换中最终得到的是一个基类对象,派生类的部分会被切掉,而对应的指针或引用的转换则并不改变派生类对象,而是将指针或引用绑定到派生类对象中,这样一来才可以实现运行时的动态绑定,如下:

    1 void get_prices(Item_Base obj, const Item_Base* pi, const Item_Base& ri)
    2 {
    3   obj.net_price(1);
    4 
    5   pi->net_price(1);
    6   ri->net_price(1);
    7 }

    上面的代码中,obj是基类对象,无论传进来的是基类对象还是派生类对象,最终都会成为基类对象,调用的也只能是基类的net_price成员函数;而第5行和第6行的指针和引用调用的函数则在运行时根据动态类型确定。所以,某些情况下,指针或引用能完成对象不能完成的任务,有更好的适用性,但是使用指针或引用会增加用户的负担,比如指针所指对象的释放等,为此,可以使用一种通用的技术——包装类或句柄类。句柄类存储和管理基类指针,指针所指对象的类型可以是基类类型对象也可以是派生类类型对象,虚成员的行为将在运行时根据句柄实际绑定的对象类型确定,句柄的用户可以获得动态行为且无须担心指针的管理。

    (2)指针型句柄

    句柄类包装了继承层次,如定义句柄类Sales_Item来包装继承如下继承层次:

    1 class Bulk_Item : public Item_Base
    2 {
    3 //...
    4 };

    通常句柄类Sales_Item需要三个构造函数:默认构造函数,复制构造函数和接受Item_Base对象的构造函数。当复制Sales_Item对象或给Sales_Item对象赋值时,将复制指针而不复制对象。指针型句柄类使用计数来管理副本,如图所示,Sales_Item类有两个数据成员,都是指针:一个指针指向Item_Base对象,另一个指向使用计数,多个Sales_Item对象可以共享同一计数器:

    (3)复制未知类型的对象给句柄类对象

    如上面所示,句柄类通常有一个接受Item_Base对象的构造函数,这个对象可能是基类类型对象也可能是派生类类型对象,因而这个构造函数需要在不知道对象的确切类型时分配已知对象的副本。为了支持这种情况,需要从基类开始,在继承层次的每个类型中增加clone,并将该函数定义为虚函数:

     1 class Item_Base
     2 {
     3 public:
     4   virtual Item_Base* clone() const
     5   {
     6     return new Item_Base(*this);
     7   }
     8 //...
     9 };
    10 
    11 class Bulk_Item : public Item_Base
    12 {
    13 public:
    14   Bulk_Item* clone() const 
    15   {
    16     return new Bulk_Item(*this);
    17   }
    18 //...
    19
    };

    对应的句柄类中的构造函数:

    1 Sales_Item::Sales_Item(const Item_Base& item)
    2 :p(item.clone()),use(new size_t(1))
    3 {
    4 }

    (4)句柄类定义

    句柄类使用计数来表示有多少个句柄类在使用同一Item_Base对象(也可以是派生类对象),每通过复制或赋值产生一个新的Sales_Item副本,计数加1,注意赋值操作中计数的变化,应使其也适合自赋值的情形;句柄类还应定义解引用操作符和箭头操作符。句柄类Sales_Item的定义如下:

     1 #ifndef SALES_ITEM
     2 #define SALES_ITEM
     3 
     4 #include "Item_Base.h"
     5 #include "Bulk_Item.h"
     6 
     7 class Sales_Item
     8 {
     9 public:
    10     Sales_Item()
    11         :p(0), use(new size_t(1))
    12     {
    13     }
    14     Sales_Item(const Item_Base& item)
    15         :p(item.clone()), use(new size_t(1))
    16     {
    17     }
    18     Sales_Item(const Sales_Item& i)
    19         :p(i.p),use(i.use)
    20     {
    21         ++*use;
    22     }
    23     Sales_Item& operator=(const Sales_Item& rhs)
    24     {
    25         ++*rhs.use;
    26         decr_use();
    27         p = rhs.p;
    28         use = rhs.use;
    29         return *this;
    30     }
    31     const Item_Base* operator->() const
    32     {
    33         if(p)
    34             return p;
    35     }
    36     const Item_Base& operator*() const
    37     {
    38         if(p)
    39             return *p;
    40     } 
    41 private:
    42     Item_Base *p;
    43     size_t *use;
    44     void decr_use
    45     {
    46         if(--*use == 0)
    47         {
    48             delete p;
    49             delete use;
    50         }
    51     }
    52 };
    53 
    54 #endif
    View Code
  • 相关阅读:
    node递归批量重命名指定文件夹下的文件
    nvm
    node在Web中的用途
    给flash续命(rtmp/http-flv网页播放器)
    AMR/PCM格式语音采集/编码/转码/解码/播放
    视频分析,目标跟踪应用方案梳理
    srs-librtmp pusher(push h264 raw)
    srs
    nginx-rtmp/http-flv
    Introduction to Sound Programming with ALSA
  • 原文地址:https://www.cnblogs.com/sophia-yun/p/3163683.html
Copyright © 2020-2023  润新知