一个泛型句柄类--C++模板和泛型编程--c++ primer

16.5. 一个泛型句柄类

这个例子体现了 C++ 相当复杂的语言应用,理解它需要很好地理解继承和模板。在熟悉了这些特性之后再研究这个例子也许会帮助。另一方面,这个例子还能很好地测试你对这些我的理解程序。

在第十五章定义了两个句柄类:Sales_item 类(第 15.8 节)和 Query 类(第 15.9 节)。这两个类管理继承层次中对象的指针,句柄的用户不必管理指向这些对象的指针,用户代码可以使用句柄类来编写。句柄能够动态分配和释放
相关继承类的对象,并且将所有“实际”工作转发给继承层次中的底层类。这两个句柄类似但并不相同:类似之处在于都定义了使用计数式的复制控制,管理指向继承层次中某类型对象的指针;不同之处在于它们提供给继承层次用户的接口。
两个类的使用计数的实现是相同的。这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数。原本不相关的 Sales_item 类型和 Query类型,可通过使用该模板进行公共的使用计数工作面得以简化。至于是公开还是
隐藏下层的继承层次,句柄可以保持不同。
本节将实现一个泛型句柄类(generic handle class),提供管理使用计数和基础对象的操作。然后,我们重新编写 Sales_item 类,展示它怎样使用泛型句柄而不是定义自己的使用计数操作。

16.5.1. 定义句柄类
Handle 类行为类似于指针:复制 Handle 对象将不会复制基础对象,复制之后,两个 Handle 对象将引用同一基础对象。要创建 Handle 对象,用户需要传递属于由 Handle 管理的类型(或从该类型派生的类型)的动态分配对象的地址,从此刻起,Handle 将“拥有”这个对象。而且,一旦不再有任意 Handle 对象与该对象关联,Handle 类将负责删除该对象。
对于这一设计,我们的泛型 Handle 类的实现如下:


     template <class T> class Handle {
     public:
         // unbound handle
         Handle(T *p = 0): ptr(p), use(new size_t(1)) { }

         // overloaded operators to support pointer behavior
         T& operator*();
         T* operator->();
         const T& operator*() const;
         const T* operator->() const;
         // copy control: normal pointer behavior, but last Handle
deletes the object
         Handle(const Handle& h): ptr(h.ptr), use(h.use)
                                             { ++*use; }
         Handle& operator=(const Handle&);
         ~Handle() { rem_ref(); }
     private:
         T* ptr;          // shared object
         size_t *use;     // count of how many Handle spointto *ptr
         void rem_ref()
             { if (--*use == 0) { delete ptr; delete use; } }
     };

这个类看来与其他句柄类似,赋值操作符也类似。

   template <class T>
     inline Handle<T>& Handle<T>::operator=(const Handle &rhs)
     {
         ++*rhs.use;      // protect against self-assignment
         rem_ref();       // decrement use count and delete pointers if
needed
         ptr = rhs.ptr;
         use = rhs.use;
         return *this;
     }

Handle 类将定义的其他成员是解引用操作符和成员访问操作符,这些操作符将用于访问基础对象。让这些操作检查 Handle 是否确实绑定到对象,可以提供一种安全措施。如果 Handle 没有绑定到对象,则试图访问对象将抛出一个异常。
这些操作的非 const 版本看来如下所示:

     template <class T> inline T& Handle<T>::operator*()
     {
         if (ptr) return *ptr;
         throw std::runtime_error
                        ("dereference of unbound Handle");

     }
     template <class T> inline T* Handle<T>::operator->()
     {
         if (ptr) return ptr;
         throw std::runtime_error
                        ("access through unbound Handle");
     }

实现一个 Handle 类的自己的版本。
Exercises Section 16.5.1
Exercise
16.45:
实现一个 Handle 类的自己的版本。
Exercise
16.46:
解释复制 Handle 类型的对象时会发生什么。
Exercise
16.47:
Handle 类对用来实例化实际 Handle 类的类型有限制吗?如果有,限制有哪些?
Exercise
16.48:
解释如果用户将 Handle 对象与局部对象关联会发生什么。解释如果用户删除 Handle 对象所关联的对象会发生
什么。

16.5.2. 使用句柄
我们希望 Handle 类能够用于其他类的内部实现中。但是,为了帮助理解Handle 类怎样工作, 交首先介绍一个较简单的例子。 这个例子通过分配一个 int对象, 并将一个 Handle 对象绑定到新分配的 int 对象而说明 Handle 的行为:

   { // new scope
       // user allocates but must not delete the object to which the Handle
is attached
       Handle<int> hp(new int(42));
       { // new scope
           Handle<int> hp2 = hp; // copies pointer; use count incremented
           cout << *hp << " " << *hp2 << endl; // prints 42 42
           *hp2 = 10;           // changes value of shared underlying int

       }   // hp2 goes out of scope; use count is decremented
       cout << *hp << endl; // prints 10
     } // hp goes out of scope; its destructor deletes the int

即使是 Handle 的用户分配了 int 对象,Handle 析构函数也将删除它。在外层代码块末尾最后一个 Handle 对象超出作用域时,删除该 int 对象。为了访问基础对象,应用了 Handle 的 * 操作符,该操作符返回对基础 int 对象的引用。
使用 Handle 对象对指针进行使用计数作为在类实现中使用 Handle 的例子,可以重新实现 Sales_item 类(第15.8.1 节),该类的这个版本定义相同的接口,但可以通过用Handle<Item_base>: 对象代替 Item_base 指针而删去复制控制成员:

  class Sales_item {
     public:
         // default constructor: unbound handle
         Sales_item(): h() { }
         // copy item and attach handle to the copy
         Sales_item(const Item_base &item): h(item.clone()) { }
         // no copy control members: synthesized versions work
         // member access operators: forward their work to the Handle
class
         const Item_base& operator*() const { return *h; }
         const Item_base* operator->() const
                                { return h.operator->(); }
     private:
         Handle<Item_base> h; // use-counted handle
     };

虽然 Sales_item 类的接口没变,它的实现与原来的相当不同:
• 两个类都定义了默认构造函数和以 Item_base 对象为参数和 const 引用的构造函数。
• 两个类都将重载的 * 和 -> 操作符定义为 const 成员。
基于 Handle 的 Sales_item 版本有一个数据成员,该数据成员是关联传给构造函数的 Item_base 对象的副本上的 Handle 对象。因为 Sales_item 的这个版本没有指针成员,所以不需要复制控制成员,Sales_item 的这个版本可以
安全地使用合成的复制控制成员。管理使用计数和相关 Item_base 对象的工作在 Handle 内部完成。

因为接口没变, 所以不需要改变使用 Sales_item 类的代码。 例如, 第 15.8.3节中编写的程序可以无须改变而使用:

  double Basket::total() const
     {
         double sum = 0.0; // holds the running total

         for (const_iter iter = items.begin();
                         iter != items.end();
                         iter = items.upper_bound(*iter))
         {
             // we know there's at least one element with this  key in the
Basket
             // virtual call to net_priceapplies appropriate discounts,
if any
             sum += (*iter)->net_price(items.count(*iter));
         }
         return sum;
     }

调用 net_price 函数的语句值得仔细分析一下:

     sum += (*iter)->net_price(items.count(*iter));

这个语句使用 -> 操作符获取并运行 net_price 函数, 重要的是理解这个操
作符怎样工作:
• (*iter) 返回 h,h 是使用计数式句柄的成员。
• 因此,(*iter)-> 使用句柄类的重载箭头操作符。
• 编译器计算 h.operator->(), 获得该 Handle 对象保存的 Item_base 指
针。
• 编译器对该 Item_base 指针解引用,并调用指针所指对象的 net_price
成员。

Exercises Section 16.5.2
Exercise
16.49:
实现本节提出的 Sales_item 句柄的版本,该版本使用泛型 Handle 类管理 Item_base 指针。
Exercise
16.50:
重新运行函数计算销售总额。列出让你的代码工作必须进行的所有修改。
Exercise
16.51:
重新编写 Section 15.9.4 第 15.9.4 节的 Query 类以使用泛型 Handle 类。注意你需要将 Handle 类设为
Query_base 类的友元,以使它能够访问 Query_base 构造函数。列出并解释让程序工作要做的其他所有修改。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZenZenZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值