首页 > 代码库 > C++ Primer 学习笔记_72_面向对象编程 --句柄类与继承[续]

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

面向对象编程

--句柄类与继承[]



三、句柄的使用

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



1、比较两个Sales_item对象

在编写函数计算销售总数之前,需要定义比较Sales_item对象的方法。要用Sales_item作为关联容器的关键字,必须能够比较它们。关联容器默认使用关键字类型的小于操作符,但是如果给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循环,否则,就处理下一本书。

   我们使用multisetcount成员确定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;
}