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 handleHandle(T *p = 0): ptr(p), use(new size_t(1)) { }// overloaded operators to support pointer behaviorT& operator*();T* operator->();const T& operator*() const;const T* operator->() const;// copy control: normal pointer behavior, but last Handle
deletes the objectHandle(const Handle& h): ptr(h.ptr), use(h.use){ ++*use; }Handle& operator=(const Handle&);~Handle() { rem_ref(); }private:T* ptr;          // shared objectsize_t *use;     // count of how many Handle spointto *ptrvoid 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-assignmentrem_ref();       // decrement use count and delete pointers if
neededptr = 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 attachedHandle<int> hp(new int(42));{ // new scopeHandle<int> hp2 = hp; // copies pointer; use count incrementedcout << *hp << " " << *hp2 << endl; // prints 42 42*hp2 = 10;           // changes value of shared underlying int}   // hp2 goes out of scope; use count is decrementedcout << *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 handleSales_item(): h() { }// copy item and attach handle to the copySales_item(const Item_base &item): h(item.clone()) { }// no copy control members: synthesized versions work// member access operators: forward their work to the Handle
classconst 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 totalfor (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 anysum += (*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 构造函数。列出并解释让程序工作要做的其他所有修改。

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

  1. 设计一个Windows应用程序,要求如下: 构造一个产品基类。 分别定义家电、日用百货、衣服等派生类,具体要求有不同的特征和行为。 定义一个泛型货架类,约束参数类型为产品

    设计一个Windows应用程序,要求如下: 构造一个产品基类. 分别定义家电.日用百货.衣服等派生类,具体要求有不同的特征和行为. 定义一个泛型货架类,约束参数类型为产品类.该泛型的货架类包括一个泛型 ...

  2. C#设计一个Windows应用程序,要求如下。 ①构造一个产品基类。 ②分别定义家电、日用百货、衣服等派生类,要求具有不同的特征和行为。 ③定义一个泛型货架类,约束参数类型为产品类。该泛型的货架类包

    设计一个Windows应用程序,要求如下. 1.构造一个产品基类. 2.分别定义家电.日用百货.衣服等派生类,要求具有不同的特征和行为. 3.定义一个泛型货架类,约束参数类型为产品类.该泛型的货架类包 ...

  3. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板特例化

    1. 定义一个特例化版本, 模板参数为 Sales data // 打开std 命名空间, 以便特例化 std::hash namespace std {template struct hash< ...

  4. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板和static

    1. 定义 与任何其他static数据成员相同,模板类的每个static数据成员必须有且仅有一个定义.类模板的每个实例都有一个独有的static对象. eg. Foo是一个类模板 有一个名为count ...

  5. C++ Primer 5th笔记(chap 16 模板和泛型编程) 类模板的成员函数

    1. 分类 1.1 定义在类模板内的成员函数 被隐式声明为内联函数. 1.2. 类模板外部的成员函数, 类模板的成员函数本身是一个普通函数.但是,类模板的每个实例都有其自己版本的成员函数.因此,类模板 ...

  6. C++ Primer 5th笔记(chap 16 模板和泛型编程)类模板定义

    1. 定义 类似函数模板,类模板以关键字template开始,后跟模板参数列表.在类模板(及其成员)的定义中,我们将模板参数当作替身,代替使用模板时用户需要提供的类型或值: template < ...

  7. C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

    模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模 ...

  8. C++ Primer学习笔记-----第十六章:模板与泛型编程

    模板是C++中泛型编程的基础. 模板是蓝图,用来创建类型,创建的类型就是模板的实例,就好像我们用一个类型创建相应的实例一样. 函数模板 template<typename T> //模板参 ...

  9. C++ 模板与泛型编程简述

    目录 1.什么是模板和泛型编程 2.定义及使用模板 1.什么是模板和泛型编程 什么是模板?什么是泛型编程?模板的概念与泛型编程是相辅相成的.想象一个场景:我们需要比较两个整数或两个字符串的大小,假如你 ...

  10. 《Effective C++》 总结篇(模板与泛型编程)

    条款四十一:了解隐式接口的和编译期多态 和面向对象编程的显示接口和运行期多态不同, 泛型编程更多是隐式接口和编译期多态. #include <iostream> using namespa ...

最新文章

  1. etcd 访问 锁_在系统中用etcd实现服务注册和发现
  2. 创新实训个人记录:metric k-center
  3. 2020华南理工计算机考研分数线,华南理工大学2020考研复试分数线已公布
  4. Ubuntu ssh 服务开启方法
  5. 富满电子鸿蒙系统,电子行业周报:HARMONYOS+2.0助力AIOT生态体系发展
  6. 机器学习(二十四)——数据不平衡问题, 强化学习
  7. 测试电梯的测试用例_【转】电梯功能的测试用例和测试方案
  8. Java 8功能接口–实现Scala类型的随机思维
  9. MAC配置JCO,与找不到sapjco3异常
  10. 输入流IS和输出流OS学习总结
  11. 诗与远方:无题(九十一)
  12. C++求字符串长度的两种方法
  13. 2018下半年软考报名
  14. Hibernate之集合映射
  15. 单片机算法c语言程序,51单片机PID的算法实现程序C语言
  16. 主控芯片测试软件,主控芯片检测工具MyDiskTest的使用教程的详解【图文】
  17. 在Macbook Pro 上安装refind以引导os x + ubuntu双系统
  18. WIFI通信-笔记整理
  19. 为什么会有口臭,如何避免?
  20. 四轴码垛机器人MDH模型运动学

热门文章

  1. Learun敏捷框架甘特图——摆脱项目管理的泥沼
  2. 人工智能与安全论坛:智能与安全的融合与对抗
  3. UDS诊断系列介绍03-DCM
  4. 这样的科幻不该被埋没,吐血推荐!
  5. px和毫米的换算_像素跟毫米换算(像素和毫米换算器)
  6. docker 安装 Redis5.X
  7. LeetCode 中文刷题手册:LeetCode Cookbook下载
  8. 网易云音乐 网络错误 linux,Linux版网易云音乐播放音乐时无限显示“网络错误”的解决办法...
  9. 谷歌在线工具--json editor online
  10. 决策树算法 (CART分类树)