标签:
引言:
【小心地雷】
这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板。在熟悉了这些特性之后再研究这个例子也许会帮助。另一方面,这个例子还能很好地测试你对这些特性的理解程度。
前面示例的Sales_item和Query两个类的使用计数的实现是相同的。这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数。原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化。至于是公开还是隐藏下层的继承层次,句柄可以保持不同。
一、定义句柄类
Handle类行为类似于指针:复制Handle对象将不会复制基础对象,复制之后,两个Handle对象将引用同一基础对象。要创建Handle对象,用户需要传递属于由Handle管理的类型(或从该类型派生的类型)的动态分配对象的地址,从此刻起,Handle将“拥有”这个对象。而且,一旦不再有任意Handle对象与该对象关联,Handle类将负责删除该对象。
Handle类:
template <class T> class Handle
{
public:
Handle(T *p = 0):ptr(p),use(new size_t(1)) {}
Handle(const Handle &h):ptr(h.ptr),use(h.use)
{
++ *use;
}
Handle &operator=(const Handle &rhs);
~Handle()
{
rem_ref();
}
T &operator*();
T *operator->();
const T &operator*() const;
const T *operator->() const;
private:
T *ptr;
size_t *use;
void rem_ref()
{
if (-- *use == 0)
{
delete use;
delete ptr;
}
}
};
赋值操作符:
template <class Type>
Handle<Type> &Handle<Type>::operator=(const Handle<Type> &rhs)
{
++ *rhs.use;
rem_ref();
ptr = rhs.ptr;
use = rhs.use;
return *this;
}
解引用操作符和成员访问操作符[+P562习题16.45]:
如果Handle没有绑定到对象,则试图访问对象时将抛出一个异常:
template <typename Type>
Type &Handle<Type>::operator*()
{
if (ptr)
{
return *ptr;
}
throw std::runtime_error("dereference of unbound Handle");
}
template <typename Type>
Type *Handle<Type>::operator->()
{
if (ptr)
{
return ptr;
}
throw std::runtime_error("access through unbound Handle");
}
template <typename Type>
const Type &Handle<Type>::operator*() const
{
if (ptr)
{
return *ptr;
}
throw std::runtime_error("dereference of unbound Handle");
}
template <typename Type>
const Type *Handle<Type>::operator->() const
{
if (ptr)
{
return ptr;
}
throw std::runtime_error("access through unbound Handle");
}
二、使用句柄
我们希望Handle类能够用于其他类的内部实现中。
一个简单的示例:通过分配一个int对象,并将一个Handle对象绑定到新分配的int对象而说明Handle的行为:
{
Handle<int> hp(new int(42));
{
Handle<int> hp2 = hp;
cout << *hp << " " << *hp2 << endl; //42 42
*hp2 = 10;
}
cout << *hp << endl; //10
}
即使是Handle的用户分配了int对象,Handle析构函数也将删除它。在外层代码末尾最后一个Handle对象超出作用域时,删除该int对象。
使用Handle对象对指针进行使用计数
可以重新实现Sales_item类,在类中使用Handle,该类的这个版本定义相同的接口,但可以通过用Handle<Item_base>对象代替Item_base指针而删除复制控制成员:
class Sales_item
{
public:
Sales_item():h() {}
Sales_item(const Item_base &item):h(item.clone()) {}
const Item_base &operator*() const
{
return *h;
}
const Item_base *operator->() const
{
return h.operator -> ();
}
private:
Handle<Item_base> h;
};
因为Sales_item的这个版本没有指针成员,所以不需要复制控制成员,Sales_item的这个版本可以安全的使用合成的复制控制成员。管理使用计数和相关Item_base对象的工作在Handle内部完成。
因为接口没变,所以不需要改变使用Sales_item类的代码。如:
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));
}
return sum;
}
分析:
sum += (*iter)->net_price(items.count(*iter));
1)(*iter)返回h,h是使用计数式句柄的成员;
2)因此,(*iter)->使用句柄类的重载箭头操作符;
3)编译器计算h.operator->(),获得Handle对象保存的Item_base指针;
4)编译器对该Item_base指针解引用,并调用指针所指向对象的Item_base成员。
//P564 习题16.51
class Query
{
friend Query operator~(const Query &);
friend Query operator|(const Query &,const Query &);
friend Query operator&(const Query &,const Query &);
public:
Query(const string &);
set<TextQuery::line_no> eval(const TextQuery &t) const
{
return h -> eval(t);
}
ostream &display(ostream &os) const
{
return h -> display();
}
private:
Query(Query_base *query):h(query) {}
Handle<Query_base> h;
};
/**
*其他操作与前相似,在此不再赘述
*/C++ Primer 学习笔记_83_模板与泛型编程 -一个泛型句柄类
标签:
原文地址:http://blog.csdn.net/selfi_xiaowen/article/details/51336429