
一、指针型句柄
其实说白了句柄类一点都不神秘,前面我们接触过“计数类”用来管理指针,这里的句柄类类似,但是比简单的计数类增加了一些其他的功能,因为我们除了利用它管理指针,还希望便捷的使用其指向的对象。这里的主要idea就是将指针的管理工作封装到一个类中,类中起码具有两个数据成员,一个指向对象的指针和一个计数指针,计数归零时意味着要释放对象和句柄类。这里我们使用的例子还是上一节中的图书类,这里简单再次给出:
点击(此处)折叠或打开
-
//图书基类
-
class Item_base
-
{
-
public:
-
Item_base(const std::string &book = "",
-
double sales_price = 0.0):
-
book_no(book), price(sales_price) {}
-
std::string book() const {return book_no;}
-
virtual double net_price(std::size_t n) const
-
{return n*price;}
-
virtual ~Item_base() {}
-
private:
-
std::string book_no;
-
protected:
-
double price;
-
-
} ;
-
//含有折扣的图书类,必须满足最小的min_qyt数量要求才可以享有折扣
-
class Bulk_item : public Item_base
-
{
-
public:
-
double net_price(std::size_t) const;
-
private:
-
std::size_t min_qyt;
-
double discount;
-
}
-
double Bulk_item::net_price(std::size_t cnt) const
-
{
-
if (cnt >= min_qty)
-
return cnt * (1 - discount ) * price;
-
else
-
return cont * price;
- }
点击(此处)折叠或打开
-
Handle_item item(Bulk_item("0-201-42778-2", 42, 2, 201));
- item->net_price() //对net_price函数的虚调用
点击(此处)折叠或打开
- //定义一个句柄类
-
class Handle_item
-
{
-
public:
-
Handle_item():p(0), cnt(new std::size_t(1)) {} //默认构造函数
-
Handle_item(const Item_base&); //自定义的接受基类引用为参数,进行绑定的构造函数
-
Handle_item(const Handle_item &i): //复制构造函数
-
p(i.p), cnt(i.cnt) {++*cnt;}
-
~Handle_item() {des_use();} //析构函数
-
Handle_item& operator=(const Handle_item); //定义赋值操作
-
-
const Handle_item *oprator->()const { //定义->运算符
-
if (p) return p;
-
else
-
throw std::logic_error("未绑定Handle_item"));
-
)
-
const Handle_item &operator*() const { //定义解引用运算符
-
if (p) return p;
-
else
-
throw std::logic_error("未绑定Handle_item");
-
}
-
private:
-
Item_base *p;
-
std::size_t *cnt;
-
void des_use()
-
{
-
if (--*cnt == 0)
-
{
-
delete p;
-
delete cnt;
-
}
-
}
-
-
- }
点击(此处)折叠或打开
-
Handle_item&
-
Handle_item::oprator=(const Handle_item &rhs)
-
{
-
++*rhs.use;
-
des_use();
-
p = rhs.p;
-
use = rhs.use;
-
return *this;
- }
实现构造函数的时候,特别说明接受Item_base引用绑定一个句柄类的构造函数,因为这里存在的问题是Item_base引用的实际对象类型只有在运行时才能够确定,那如何为其分配空间呢?解决的思路是:既然参数是动态绑定,那么我们的行为也实现动态绑定。实现动态绑定的方法自身是使用虚函数,这就需要我们定义一个新的虚函数clone(),从而能够实际的对象类型开辟合适的空间。为了使用虚函数clone(),我们为基类和派生类添加函数:
点击(此处)折叠或打开
- Item_base::
-
virtual Item_base* clone() const {return new Item_base(*this);}
-
- Bulk_item::
- Bulk_item* clone() const {return new Bulk_item(*this);}
点击(此处)折叠或打开
-
Handle_item::Handle_item(const Item_base &item):
- p(item.clone()), cnt(new std::size_t(1)) {}
二、句柄的使用
定义了handle_item,接下来使用的时候就要将句柄类对象放入multiset容器中成为容器元素,从而实现利用指针访问商品对象的最终目的。这里有一个小问题需要解决,放入multiset中的元素必须能够比价大小从而确定排列顺序,我们不会重载<运算符,那样太费事了,我们选择的方法是直接提供一个比较函数,就比较图书的book号码就可以。
点击(此处)折叠或打开
-
inline bool
-
compare(const Handle_item &lhs, const Handle_item &rhs)
-
{
-
return lhs->book() < rhs->book();
- }
点击(此处)折叠或打开
-
class Basket {
-
typedef bool (*comp)(const Handle_item&, const Handle_item&);
-
public:
-
typedef std::multiset<Handle_item, comp> set_type;
-
typedef set_type::size_type size_type;
-
typedef set_type::const_iterator const const_iter;
-
Basket():items(compare) {}
-
void add(const Handle_item &item)
-
{
-
items.insert(item);
-
}
-
size_type size(cosnt Handle_item &i)const
-
{
-
return items.count(i);
-
}
-
double total() const;
-
private:
-
std::multiset<Handle_item, comp> items;
-
}
-
-
double Basket::total() const
-
{
-
double sum = 0.0;
-
for (const_iter iter = items.begin();
-
iter != items.end();
-
iter = items.upper_bound(*iter))
-
{
-
sum += (*iter)->net-price(items.count(*iter));
-
}
- }
PS:
最后谈谈自己对于句柄类的感觉。句柄类听名字就让自己想到了windows中的句柄概念,记得当时看的时候也只是知道那是一个数据结构,里面肯定封装了对象的指针,但是还有更多的对象属性在里面,比如访问权限控制等等。现在想来原型结构上系统的“句柄”也许与我们变成使用的句柄类有些类似吧。