C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]
面向对象编程
--句柄类与继承[续]
三、句柄的使用
使用Sales_item对象能够更easy地编写书店应用程序。代码将不必管理Item_base对象的指针,但仍然能够获得通过Sales_item对象进行的调用的虚行为。
1、比較两个Sales_item对象
在编写函数计算销售总数之前,须要定义比較Sales_item对象的方法。要用Sales_item作为关联容器的keyword,必须能够比較它们。关联容器默认使用keyword类型的小于操作符,可是假设给Sales_item定义小于操作符,将使其含义不明;
幸好,关联容器使我们能够指定一个函数[或函数对象]用作比較函数。并且,在定义容器对象时必须提供比較函数。
定义一个函数用于比較Sales_item对象:
inline bool
compare(const Sales_item &lsh,const Sales_item &rhs)
{
return lsh -> book() < rhs -> book();
}
该函数使用Sales_item的->操作符,该操作符返回Item_base对象的指针,哪个指针用于获取并执行成员的book操作,该成员返回ISBN。
2、使用带比較器的关联容器
对于比較函数,它必须作为容器的一部分而存储,不论什么在容器中添加�或查找元素的操作都要使用比較函数。
要有效地工作,关联容器须要对每一个操作使用同一比較函数。然而,期望用户每次记住比較函数是不合理的,尤其是,没有办法检查每一个调用使用同一比較函数。因此,容器记住比較函数是有意义的。通过将比較器存储在容器对象中,能够保证比較元素的每一个操作将一致地进行。
为了存储比較器,它须要知道比較器类型。形參类型也不须要与 key_type全然匹配,应该同意能够转换为key_type的随意形參类型。
所以,要使用Sales_item的比較函数,在定义multiset时必须指定比較器类型。在我们的样例中,比較器类型是接受两个constSales_item 引用并返回bool值的函数。
typedef bool (*Comp)(const Sales_item &,const Sales_item &);
将Comp定义为函数类型指针的同义词,该函数类型与我们希望用来比較 Sales_item对象的比較函数相匹配。
接着须要定义multiset,保存Sales_item类型的对象并在它的比較函数中使用这个Comp类型。关联容器的每一个构造函数使我们能够提供比較函数的名字。
能够这样定义使用compare函数的空multiset:
std::multiset<Sales_item,Comp> items(compare);
这个定义是说,items是一个multiset,它保存Sales_item对象并使用Comp类型的对象比較它们。multiset是空的—— 我们没有提供不论什么元素,但我们的确提供了一个名为compare的比較函数。当在items中添加�或查找元素时,将用compare函数对multiset进行排序。
3、容器与句柄类
我们定义一个Basket类,用以跟踪销售并计算购买价格:
class Basket
{
typedef bool (*Comp)(const Sales_item &,const Sales_item &);
public:
typedef multiset<Sales_item,Comp> set_type;
typedef set_type::size_type size_type;
typedef set_type::const_iterator const_iter; Basket():items(compare) {} void add_item(const Sales_item &item)
{
items.insert(item);
}
size_type size(const Sales_item &i) const
{
return items.count(i);
} double total() const; private:
multiset<Sales_item,Comp> items;
};
该类定义了一个构造函数,即Basket默认构造函数。该类须要自己的默认构造函数,以便将compare传递给建立items成员的multiset构造函数。
4、使用句柄执行虚函数
Basket类中的total函数用于返回购物篮中全部物品的价格:
double Basket::total() const
{
double sum = 0.0; for (set_type::iterator iter = items.begin();
iter != items.end();
iter = items.upper_bound(*iter))
{
sum += (*iter) -> net_price(items.count(*iter));
} return sum;
}
for循环中的“增量”表达式非常有意思。与读每一个元素的一般循环不同,我们推进iter指向下一个键。调用upper_bound函数以跳过与当前键匹配的全部元素,upper_bound函数的调用返回一个迭代器,该迭代器指向与iter键同样的最后一个元素的下一元素,即,该迭代器指向集合的末尾或下一本书。測试iter的新值,假设与items.end()相等,则跳出for循环,否则,就处理下一本书。
我们使用multiset的 count成员确定multiset中的多少成员具有同样的键(即,同样的isbn),并且使用该数目作为实參调用net_price函数。
for循环的循环体调用net_price函数,阅读这个调用须要一点技巧:
sum += (*iter) -> net_price(items.count(*iter));
对iter解引用获得基础Sales_item对象,对该对象应用Sales_item类重载的箭头操作符,该操作符返回句柄所关联的基础Item_base对象的指针,用该Item_base对象指针调用net_price函数,传递具有同样isbn的图书的count作为实參。net_price是虚函数,所以调用的定价函数的版本号取决于基础Item_base对象的类型。
//P511 习题15.35
//1 in item.h
#ifndef ITEM_H_INCLUDED
#define ITEM_H_INCLUDED #include <string>
#include <utility> class Item_base
{
public:
Item_base(const std::string &book = "",
double sales_price = 0.0):
isbn(book),price(sales_price) {} std::string book() const
{
return isbn;
} virtual double net_price(std::size_t n) const
{
return price * n;
} virtual Item_base *clone() const
{
return new Item_base(*this);
} virtual ~Item_base() {} private:
std::string isbn; protected:
double price;
}; class Disc_item : public Item_base
{
public:
Disc_item(const std::string &book = "",
double sales_price = 0.0,
std::size_t qty = 0,
double disc_rate = 0.0):
Item_base(book,sales_price),quantity(qty),discount(disc_rate) {} virtual double net_price(std::size_t ) const = 0; std::pair<std::size_t,double> discount_policy() const
{
return std::make_pair(quantity,discount);
} protected:
std::size_t quantity;
double discount;
}; class Bulk_item : public Disc_item
{
public:
Bulk_item(const std::string &book = "",
double sales_price = 0.0,
std::size_t qty = 0,
double disc_rate = 0.0):
Disc_item(book,sales_price,qty,disc_rate) {} virtual double net_price(std::size_t n) const
{
if (n >= quantity)
{
return n * (1 - discount) * price;
}
else
{
return n * price;
}
} virtual Bulk_item *clone() const
{
return new Bulk_item(*this);
}
}; class Lds_item : public Disc_item
{
public:
Lds_item(const std::string &book = "",
double sales_price = 0.0,
std::size_t qty = 0,
double disc_rate = 0.0):
Disc_item(book,sales_price,qty,disc_rate) {} virtual double net_price(std::size_t n) const
{
if (n <= quantity)
{
return n * (1 - discount) * price;
}
else
{
return n * price - quantity * discount * price;
}
} virtual Lds_item *clone() const
{
return new Lds_item(*this);
}
}; #endif // ITEM_H_INCLUDED
//2 in sales_item.h
#ifndef SALES_ITEM_H_INCLUDED
#define SALES_ITEM_H_INCLUDED #include "item.h"
#include <stdexcept> class Sales_item
{
public:
Sales_item():p(0),use(new std::size_t(1)) {}
Sales_item(const Item_base &rhs):
p(rhs.clone()),use(new std::size_t(1)) {} Sales_item(const Sales_item &rhs):p(rhs.p),use(rhs.use)
{
++ *use;
} ~Sales_item()
{
decr_use();
} Sales_item &operator=(const Sales_item &rhs); const Item_base *operator->() const
{
if (p)
{
return p;
}
else
{
throw std::logic_error("unbound Sales_item");
}
} const Item_base &operator*() const
{
if (p)
{
return *p;
}
else
{
throw std::logic_error("unbound Sales_item");
}
} private:
Item_base *p;
std::size_t *use; void decr_use()
{
if (-- *use)
{
delete p;
delete use;
}
}
}; #endif // SALES_ITEM_H_INCLUDED
//3 in sales_item.cpp
#include "sales_item.h" Sales_item &Sales_item::operator=(const Sales_item &rhs)
{
++ * rhs.use;
decr_use(); p = rhs.p;
use = rhs.use; return *this;
}
//4 in basket.h
#ifndef BASKET_H_INCLUDED
#define BASKET_H_INCLUDED #include "sales_item.h"
#include <set> inline bool
compare(const Sales_item &lhs,const Sales_item &rhs)
{
return lhs -> book() < rhs -> book();
} class Basket
{
typedef bool (*Comp)(const Sales_item &,const Sales_item &);
public:
typedef std::multiset<Sales_item,Comp> set_type;
typedef set_type::size_type size_type;
typedef set_type::const_iterator const_iter; Basket():items(compare){} void add_item(const Sales_item &item)
{
items.insert(item);
} size_type size(const Sales_item &item) const
{
return items.count(item);
} double total() const; private:
std::multiset<Sales_item,Comp> items; }; #endif // BASKET_H_INCLUDED
//5 in basket.cpp
#include "basket.h" double Basket::total() const
{
double sum = 0.0; for (set_type::iterator iter = items.begin();
iter != items.end();
iter = items.upper_bound(*iter))
{
sum += (*iter) -> net_price(items.count(*iter));
} return sum;
}
//6 in main.cpp
#include <iostream>
#include "item.h"
#include "basket.h" using namespace std; int main()
{
Basket basket;
Sales_item item1(Bulk_item("7-115-14554-7",99,20,0.2));
Sales_item item2(Item_base("7-115-14554-7",39));
Sales_item item3(Lds_item("7-115-14554-7",50,200,0.2));
Sales_item item4(Bulk_item("7-115-14554-7",99,20,0.2)); basket.add_item(item1);
basket.add_item(item2);
basket.add_item(item3);
basket.add_item(item4); cout << basket.total() << endl;
}
C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]的更多相关文章
- python自动化测试学习笔记-7面向对象编程,类,继承,实例变量,邮件
面向对象编程(OOP)术语: class TestClass(object): val1 = 100 def __init__(self): self.val2 = 200 ...
- C++ Primer 学习笔记_67_面向对象编程 --转换与继承、复制控制与继承
面向对象编程 --转换与继承.复制控制与继承 I.转换与继承 引言: 由于每一个派生类对象都包括一个基类部分,因此能够像使用基类对象一样在派生类对象上执行操作. 对于指针/引用,能够将派生类对象的指针 ...
- C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域
面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:假设不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这样的类作用域的层次嵌套使 ...
- python学习笔记(七):面向对象编程、类
一.面向对象编程 面向对象--Object Oriented Programming,简称oop,是一种程序设计思想.在说面向对象之前,先说一下什么是编程范式,编程范式你按照什么方式来去编程,去实现一 ...
- javascript 学习笔记之面向对象编程(一):类的实现
~~想是一回事,做是一回事,写出来又是一回事~~一直以来,从事C++更多的是VC++多一些,从面向过程到面向对象的转变,让我对OO的编程思想有些偏爱,将一个客观存在的规律抽象出来总是让人比较兴奋,通过 ...
- JavaSE学习笔记05面向对象编程01
面向对象编程01 java的核心思想就是OOP 面向过程&面向对象 面向过程思想: 步骤清晰简单,第一步做什么,第二步做什么...... 面向过程适合处理一些较为简单的问题 面向对象思想: 物 ...
- python 学习笔记7 面向对象编程
一.概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强..." ...
- javascript 学习笔记之面向对象编程(二):继承&多态
~~接上篇~~上一篇实现了类的实现以及类成员变量和方法的定义,下面我们来了解下面向对象中两个最重要的特性:继承和多态. 继承 js中同样可以实现类的继承这一面向对象特性,继承父类中的所有成员(变量和属 ...
- Spark学习笔记11面向对象编程
面向对象编程 11.1 object类 11.1.1定义一个简单的类 11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...
随机推荐
- JS几种变量交换方式以及性能分析对比
前言 "两个变量之间的值得交换",这是一个经典的话题,现在也有了很多的成熟解决方案,本文主要是列举几种常用的方案,进行大量计算并分析对比. 起由 最近做某个项目时,其中有一个需求是 ...
- 关于 eclipse启动卡死的问题 解决方法
关于 eclipse启动卡死的问题(eclipse上一次没有正确关闭,导致启动的时候卡死错误解决方法),自己常用的解决方法: 方案一(推荐使用,如果没有这个文件,就使用方案二): 到<works ...
- 修改linux系统编码
方法1: vi /etc/sysconfig/i18n 默认为: LANG="en_US.UTF-8"SYSFONT="latarcyrheb-sun16" ...
- 8-10 Coping Books uva714
题意:把一个包含m个正整数的序列划分为k个 1<=k<=m<=500的非空连续子序列 使得每个正整数恰好属于一个序列 设第i个序列的各个数之和为 Si 你的任务是让所有的 ...
- HTML Input 表单校验之datatype
凡要验证格式的元素均需绑定datatype属性,datatype可选值内置有10类,用来指定不同的验证格式. 如果还不能满足您的验证需求,可以传入自定义datatype,自定义datatype是一个非 ...
- linux/mac下命令行rm回收站--rmtrash
Linux.mac的命令行下没有回收站功能,很多时候手一抖就把重要文件给 rm -fr * 了,虽然linux下有可能通过lost +found/debugfs找回,但难度也比较大,不能保证一定能够找 ...
- 【知了堂学习笔记】java 方法重载与重写的归纳
方法重载:Java的方法重载,就是在类中可以创建多个方法,它们可以有相同的名字,但必须具有不同的参数,即或者是参数的个数不同,或者是参数的类型不同.调用方法时通过传递给它们的不同个数和类型的参数来决定 ...
- vue 组件使用中的细节点
1.is属性 有些 HTML 元素,诸如 <ul>.<ol>.<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的.而有些元 ...
- Python闭包Closure 2
由于Python中,变量作用域为LEGB,所以在函数内部可以读取外部变量,但是在函数外不能读取函数内的变量.但是出于种种原因,我们需要读取函数内的变量时候怎么办?那就是在函数内在加一个函数. def ...
- [ 转载 ] Centos安装Mysql数据库纪录
yum install mysql-community-server依赖关系错误 https://www.cnblogs.com/lzj0218/p/5724446.html rpm -qa|grep ...