面向对象编程

--句柄类与继承[续]

三、句柄的使用

使用Sales_item对象能够更easy地编写书店应用程序。代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对象进行的调用的虚行为。

1、比較两个Sales_item对象

在编写函数计算销售总数之前,须要定义比較Sales_item对象的方法。要用Sales_item作为关联容器的keyword,必须能够比較它们。关联容器默认使用keyword类型的小于操作符,可是假设给Sales_item定义小于操作符,将使其含义不明;

幸好,关联容器使我们能够指定一个函数[或函数对象]用作比較函数。并且,在定义容器对象时必须提供比較函数。

定义一个函数用于比較Sales_item对象:

  1. inline bool
  2. compare(const Sales_item &lsh,const Sales_item &rhs)
  3. {
  4. return lsh -> book() < rhs -> book();
  5. }

该函数使用Sales_item的->操作符,该操作符返回Item_base对象的指针,哪个指针用于获取并执行成员的book操作,该成员返回ISBN。

2、使用带比較器的关联容器

对于比較函数,它必须作为容器的一部分而存储,不论什么在容器中添加�或查找元素的操作都要使用比較函数。

要有效地工作,关联容器须要对每一个操作使用同一比較函数。然而,期望用户每次记住比較函数是不合理的,尤其是,没有办法检查每一个调用使用同一比較函数。因此,容器记住比較函数是有意义的。通过将比較器存储在容器对象中,能够保证比較元素的每一个操作将一致地进行。

为了存储比較器,它须要知道比較器类型。形參类型也不须要与 key_type全然匹配,应该同意能够转换为key_type的随意形參类型。

所以,要使用Sales_item的比較函数,在定义multiset时必须指定比較器类型。在我们的样例中,比較器类型是接受两个constSales_item 引用并返回bool值的函数。

  1. typedef bool (*Comp)(const Sales_item &,const Sales_item &);

将Comp定义为函数类型指针的同义词,该函数类型与我们希望用来比較 Sales_item对象的比較函数相匹配。

接着须要定义multiset,保存Sales_item类型的对象并在它的比較函数中使用这个Comp类型。关联容器的每一个构造函数使我们能够提供比較函数的名字

能够这样定义使用compare函数的空multiset:

  1. std::multiset<Sales_item,Comp> items(compare);

这个定义是说,items是一个multiset,它保存Sales_item对象并使用Comp类型的对象比較它们。multiset是空的—— 我们没有提供不论什么元素,但我们的确提供了一个名为compare的比較函数。当在items中添加�或查找元素时,将用compare函数对multiset进行排序。

3、容器与句柄类

我们定义一个Basket类,用以跟踪销售并计算购买价格:

  1. class Basket
  2. {
  3. typedef bool (*Comp)(const Sales_item &,const Sales_item &);
  4. public:
  5. typedef multiset<Sales_item,Comp> set_type;
  6. typedef set_type::size_type size_type;
  7. typedef set_type::const_iterator const_iter;
  8.  
  9. Basket():items(compare) {}
  10.  
  11. void add_item(const Sales_item &item)
  12. {
  13. items.insert(item);
  14. }
  15. size_type size(const Sales_item &i) const
  16. {
  17. return items.count(i);
  18. }
  19.  
  20. double total() const;
  21.  
  22. private:
  23. multiset<Sales_item,Comp> items;
  24. };

该类定义了一个构造函数,即Basket默认构造函数。该类须要自己的默认构造函数,以便将compare传递给建立items成员的multiset构造函数

4、使用句柄执行虚函数

Basket类中的total函数用于返回购物篮中全部物品的价格:

  1. double Basket::total() const
  2. {
  3. double sum = 0.0;
  4.  
  5. for (set_type::iterator iter = items.begin();
  6. iter != items.end();
  7. iter = items.upper_bound(*iter))
  8. {
  9. sum += (*iter) -> net_price(items.count(*iter));
  10. }
  11.  
  12. return sum;
  13. }

for循环中的“增量”表达式非常有意思。与读每一个元素的一般循环不同,我们推进iter指向下一个键。调用upper_bound函数以跳过与当前键匹配的全部元素,upper_bound函数的调用返回一个迭代器,该迭代器指向与iter键同样的最后一个元素的下一元素,即,该迭代器指向集合的末尾或下一本书。測试iter的新值,假设与items.end()相等,则跳出for循环,否则,就处理下一本书。

我们使用multiset的 count成员确定multiset中的多少成员具有同样的键(即,同样的isbn),并且使用该数目作为实參调用net_price函数。

for循环的循环体调用net_price函数,阅读这个调用须要一点技巧:

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

对iter解引用获得基础Sales_item对象,对该对象应用Sales_item类重载的箭头操作符,该操作符返回句柄所关联的基础Item_base对象的指针,用该Item_base对象指针调用net_price函数,传递具有同样isbn的图书的count作为实參。net_price是虚函数,所以调用的定价函数的版本号取决于基础Item_base对象的类型。

  1. //P511 习题15.35
  2. //1 in item.h
  3. #ifndef ITEM_H_INCLUDED
  4. #define ITEM_H_INCLUDED
  5.  
  6. #include <string>
  7. #include <utility>
  8.  
  9. class Item_base
  10. {
  11. public:
  12. Item_base(const std::string &book = "",
  13. double sales_price = 0.0):
  14. isbn(book),price(sales_price) {}
  15.  
  16. std::string book() const
  17. {
  18. return isbn;
  19. }
  20.  
  21. virtual double net_price(std::size_t n) const
  22. {
  23. return price * n;
  24. }
  25.  
  26. virtual Item_base *clone() const
  27. {
  28. return new Item_base(*this);
  29. }
  30.  
  31. virtual ~Item_base() {}
  32.  
  33. private:
  34. std::string isbn;
  35.  
  36. protected:
  37. double price;
  38. };
  39.  
  40. class Disc_item : public Item_base
  41. {
  42. public:
  43. Disc_item(const std::string &book = "",
  44. double sales_price = 0.0,
  45. std::size_t qty = 0,
  46. double disc_rate = 0.0):
  47. Item_base(book,sales_price),quantity(qty),discount(disc_rate) {}
  48.  
  49. virtual double net_price(std::size_t ) const = 0;
  50.  
  51. std::pair<std::size_t,double> discount_policy() const
  52. {
  53. return std::make_pair(quantity,discount);
  54. }
  55.  
  56. protected:
  57. std::size_t quantity;
  58. double discount;
  59. };
  60.  
  61. class Bulk_item : public Disc_item
  62. {
  63. public:
  64. Bulk_item(const std::string &book = "",
  65. double sales_price = 0.0,
  66. std::size_t qty = 0,
  67. double disc_rate = 0.0):
  68. Disc_item(book,sales_price,qty,disc_rate) {}
  69.  
  70. virtual double net_price(std::size_t n) const
  71. {
  72. if (n >= quantity)
  73. {
  74. return n * (1 - discount) * price;
  75. }
  76. else
  77. {
  78. return n * price;
  79. }
  80. }
  81.  
  82. virtual Bulk_item *clone() const
  83. {
  84. return new Bulk_item(*this);
  85. }
  86. };
  87.  
  88. class Lds_item : public Disc_item
  89. {
  90. public:
  91. Lds_item(const std::string &book = "",
  92. double sales_price = 0.0,
  93. std::size_t qty = 0,
  94. double disc_rate = 0.0):
  95. Disc_item(book,sales_price,qty,disc_rate) {}
  96.  
  97. virtual double net_price(std::size_t n) const
  98. {
  99. if (n <= quantity)
  100. {
  101. return n * (1 - discount) * price;
  102. }
  103. else
  104. {
  105. return n * price - quantity * discount * price;
  106. }
  107. }
  108.  
  109. virtual Lds_item *clone() const
  110. {
  111. return new Lds_item(*this);
  112. }
  113. };
  114.  
  115. #endif // ITEM_H_INCLUDED

  1. //2 in sales_item.h
  2. #ifndef SALES_ITEM_H_INCLUDED
  3. #define SALES_ITEM_H_INCLUDED
  4.  
  5. #include "item.h"
  6. #include <stdexcept>
  7.  
  8. class Sales_item
  9. {
  10. public:
  11. Sales_item():p(0),use(new std::size_t(1)) {}
  12. Sales_item(const Item_base &rhs):
  13. p(rhs.clone()),use(new std::size_t(1)) {}
  14.  
  15. Sales_item(const Sales_item &rhs):p(rhs.p),use(rhs.use)
  16. {
  17. ++ *use;
  18. }
  19.  
  20. ~Sales_item()
  21. {
  22. decr_use();
  23. }
  24.  
  25. Sales_item &operator=(const Sales_item &rhs);
  26.  
  27. const Item_base *operator->() const
  28. {
  29. if (p)
  30. {
  31. return p;
  32. }
  33. else
  34. {
  35. throw std::logic_error("unbound Sales_item");
  36. }
  37. }
  38.  
  39. const Item_base &operator*() const
  40. {
  41. if (p)
  42. {
  43. return *p;
  44. }
  45. else
  46. {
  47. throw std::logic_error("unbound Sales_item");
  48. }
  49. }
  50.  
  51. private:
  52. Item_base *p;
  53. std::size_t *use;
  54.  
  55. void decr_use()
  56. {
  57. if (-- *use)
  58. {
  59. delete p;
  60. delete use;
  61. }
  62. }
  63. };
  64.  
  65. #endif // SALES_ITEM_H_INCLUDED

  1. //3 in sales_item.cpp
  2. #include "sales_item.h"
  3.  
  4. Sales_item &Sales_item::operator=(const Sales_item &rhs)
  5. {
  6. ++ * rhs.use;
  7. decr_use();
  8.  
  9. p = rhs.p;
  10. use = rhs.use;
  11.  
  12. return *this;
  13. }

  1. //4 in basket.h
  2. #ifndef BASKET_H_INCLUDED
  3. #define BASKET_H_INCLUDED
  4.  
  5. #include "sales_item.h"
  6. #include <set>
  7.  
  8. inline bool
  9. compare(const Sales_item &lhs,const Sales_item &rhs)
  10. {
  11. return lhs -> book() < rhs -> book();
  12. }
  13.  
  14. class Basket
  15. {
  16. typedef bool (*Comp)(const Sales_item &,const Sales_item &);
  17. public:
  18. typedef std::multiset<Sales_item,Comp> set_type;
  19. typedef set_type::size_type size_type;
  20. typedef set_type::const_iterator const_iter;
  21.  
  22. Basket():items(compare){}
  23.  
  24. void add_item(const Sales_item &item)
  25. {
  26. items.insert(item);
  27. }
  28.  
  29. size_type size(const Sales_item &item) const
  30. {
  31. return items.count(item);
  32. }
  33.  
  34. double total() const;
  35.  
  36. private:
  37. std::multiset<Sales_item,Comp> items;
  38.  
  39. };
  40.  
  41. #endif // BASKET_H_INCLUDED

  1. //5 in basket.cpp
  2. #include "basket.h"
  3.  
  4. double Basket::total() const
  5. {
  6. double sum = 0.0;
  7.  
  8. for (set_type::iterator iter = items.begin();
  9. iter != items.end();
  10. iter = items.upper_bound(*iter))
  11. {
  12. sum += (*iter) -> net_price(items.count(*iter));
  13. }
  14.  
  15. return sum;
  16. }

  1. //6 in main.cpp
  2. #include <iostream>
  3. #include "item.h"
  4. #include "basket.h"
  5.  
  6. using namespace std;
  7.  
  8. int main()
  9. {
  10. Basket basket;
  11. Sales_item item1(Bulk_item("7-115-14554-7",99,20,0.2));
  12. Sales_item item2(Item_base("7-115-14554-7",39));
  13. Sales_item item3(Lds_item("7-115-14554-7",50,200,0.2));
  14. Sales_item item4(Bulk_item("7-115-14554-7",99,20,0.2));
  15.  
  16. basket.add_item(item1);
  17. basket.add_item(item2);
  18. basket.add_item(item3);
  19. basket.add_item(item4);
  20.  
  21. cout << basket.total() << endl;
  22. }

C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]的更多相关文章

  1. python自动化测试学习笔记-7面向对象编程,类,继承,实例变量,邮件

    面向对象编程(OOP)术语: class TestClass(object):   val1 = 100       def __init__(self):     self.val2 = 200   ...

  2. C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承

    面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...

  3. C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域

    面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...

  4. python学习笔记(七):面向对象编程、类

    一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一 ...

  5. javascript 学习笔记之面向对象编程(一):类的实现

    ~~想是一回事,做是一回事,写出来又是一回事~~一直以来,从事C++更多的是VC++多一些,从面向过程到面向对象的转变,让我对OO的编程思想有些偏爱,将一个客观存在的规律抽象出来总是让人比较兴奋,通过 ...

  6. JavaSE学习笔记05面向对象编程01

    面向对象编程01 java的核心思想就是OOP 面向过程&面向对象 面向过程思想: 步骤清晰简单,第一步做什么,第二步做什么...... 面向过程适合处理一些较为简单的问题 面向对象思想: 物 ...

  7. python 学习笔记7 面向对象编程

    一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...

  8. javascript 学习笔记之面向对象编程(二):继承&多态

    ~~接上篇~~上一篇实现了类的实现以及类成员变量和方法的定义,下面我们来了解下面向对象中两个最重要的特性:继承和多态. 继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属 ...

  9. Spark学习笔记11面向对象编程

    面向对象编程   11.1 object类 11.1.1定义一个简单的类   11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...

随机推荐

  1. Java第三阶段学习(二、IO流--------递归,字节流Stream)

    一.递归 定义:指方法在方法内调用自己 适用于方法的运算主体不变,但运行的时候,参与运算的方法参数会变化注意:一定要给递归一个出口,否则内存溢出 练习题1:使用递归打印文件夹中所有的文件,包含子目录中 ...

  2. linux中shell,awk,sed截取字符串方法总结

    转自:http://www.cnblogs.com/kinga/p/5772566.html Shell 第一种: ${parameter%word} 最小限度从后面截掉word${parameter ...

  3. HDU 1028 HDU 1398 (母函数)

    题意:输入一个n  给出其所有组合数 如: 4 = 4;  4 = 3 + 1;  4 = 2 + 2;  4 = 2 + 1 + 1;  4 = 1 + 1 + 1 + 1; 重复不算 母函数入门题 ...

  4. CentOS 5.x上配置JBoss6.x步骤图解教程

    1.如何远程连接CentOS和文件上传下载 使用工具Xmanager下的Xbroswer 首先在Xbroswer下的Xshell下新建文件夹JavaPlatServer,新建一个Xshell Sess ...

  5. Python 多线程 实例

    多线程实例 import threading import time def eat(): eatTime = time.time() for i in range(30): print('count ...

  6. 基于spring-boot的应用程序的单元测试方案

    概述 本文主要介绍如何对基于spring-boot的web应用编写单元测试.集成测试的代码. 此类应用的架构图一般如下所示: 我们项目的程序,对应到上图中的web应用部分.这部分一般分为Control ...

  7. eclipse 修改js文件无法编译到项目中

    1.场景重现 在今天修改js文件完善功能时,发现在eclipse中修改了文件后,刷新页面功能无法同步: 2.分析原因 查看编译路径,文件没有修改: 2.1 可能是缓存问题: 2.2 项目未编译: 3. ...

  8. 机器学习之路: python线性回归 过拟合 L1与L2正则化

    git:https://github.com/linyi0604/MachineLearning 正则化: 提高模型在未知数据上的泛化能力 避免参数过拟合正则化常用的方法: 在目标函数上增加对参数的惩 ...

  9. python 乘法表、打印菱形

    for i in range(1,10): print ' '.join(map(lambda x:"%d x %d = %d"%(x,i,i*x),range(1,i+1))) ...

  10. CF17E Palisection 差分+manacher算法

    题目大意: 给定一个串$S$,询问有多少对相交的回文子串 直接做的办法: 我们先考虑求出以$i$为结尾的串的数量,这个很好统计 之后,我们再求出所有包含了点$i$的回文串的数目 这个相当于在$i$的左 ...