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_面向对象编程 --句柄类与继承[续],布布扣,bubuko.com

时间: 2024-12-09 10:06:42

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

C++ Primer 学习笔记_71_面向对象编程 --句柄类与继承

面向对象编程 --句柄类与继承 引言: C++中面向对象编程的一个颇具讽刺意味的地方是:不能使用对象支持面向对象编程,相反,必须使用指针或引用. void get_prices(Item_base object, Item_base *pointer, Item_base &reference){ //需要根据指针或引用实际所绑定的类型进行调用 cout<< pointer ->net_price(1)<< endl; cout<< reference.n

C++ Primer 学习笔记_68_面向对象编程 --构造函数和复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

C++ Primer 学习笔记_68_面向对象编程 -构造函数跟复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

C++ Primer 学习笔记_73_面向对象编程 --再谈文本查询示例

面向对象编程 --再谈文本查询示例 引言: 扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询. 为了说明问题,将用下面的简单小说来运行查询: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he

C++ Primer 学习笔记_74_面向对象编程 --再谈文本查询示例[续/习题]

面向对象编程 --再谈文本查询示例[续/习题] //P522 习题15.41 //1 in TextQuery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <set> #include <map&g

C++ Primer 学习笔记_66_面向对象编程 --定义基类和派生类[续]

算法旨在用尽可能简单的思路解决问题,理解算法也应该是一个越看越简单的过程,当你看到算法里的一串概念,或者一大坨代码,第一感觉是复杂,此时不妨从例子入手,通过一个简单的例子,并编程实现,这个过程其实就可以理解清楚算法里的最重要的思想,之后扩展,对算法的引理或者更复杂的情况,对算法进行改进.最后,再考虑时间和空间复杂度的问题. 了解这个算法是源于在Network Alignment问题中,图论算法用得比较多,而对于alignment,特别是pairwise alignment, 又经常遇到maxim

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数、抽象类、虚析构函数、动态创建对象

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数.抽象类.虚析构函数.动态创建对象 一.纯虚函数 1.虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 2.如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 3.在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做 4.定义纯虚函数: class <类名> { virtual <类型> <函

C++ Primer 学习笔记_65_面向对象编程 --概述、定义基类和派生类

面向对象编程 --概述.定义基类和派生类 引言: 面向对象编程基于的三个基本概念:数据抽象.继承和动态绑定. 在C++中,用类进行数据抽象,用类派生从一个类继承另一个:派生类继承基类的成员.动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数. 继承和动态绑定在两个方面简化了我们的程序:[继承]能够容易地定义与其他类相似但又不相同的新类,[派生]能够更容易地编写忽略这些相似类型之间区别的程序. 面向对象编程:概述 面向对象编程的关键思想是多态性(polymorphism)

C++ Primer 学习笔记33_面向对象编程(4)--虚函数与多态(一):多态、派生类重定义、虚函数的访问、 . 和-&gt;的区别、虚析构函数、object slicing与虚函数

C++ Primer学习笔记33_面向对象编程(4)--虚函数与多态(一):多态.派生类重定义.虚函数的访问. . 和->的区别.虚析构函数.object slicing与虚函数 一.多态 多态可以简单地概括为"一个接口,多种方法",前面讲过的重载就是一种简单的多态,一个函数名(调用接口)对应着几个不同的函数原型(方法). 更通俗的说,多态行是指同一个操作作用于不同的对象就会产生不同的响应.或者说,多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态行分