C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程

--一个泛型句柄类



引言:

【小心地雷】

这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承模板。在熟悉了这些特性之后再研究这个例子也许会帮助。另一方面,这个例子还能很好地测试你对这些特性的理解程度。



前面示例的Sales_itemQuery两个类的使用计数的实现是相同的。这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数。原本不相关的Sales_item类型和Query类型,可通过使用该模板进行公共的使用计数工作而得以简化。至于是公开还是隐藏下层的继承层次,句柄可以保持不同。



一、定义句柄类

Handle类行为类似于指针:复制Handle对象不会复制基础对象,复制之后,两个Handle对象将引用同一基础对象。要创建Handle对象,用户需要传递属于Handle管理的类型(或从该类型派生的类型)动态分配对象的地址,从此刻起,Handle将“拥有”这个对象。而且,一旦不再有任意Handle对象与该对象关联,Handle类将负责删除该对象

Handle类:

  1. template <class T> class Handle  
  2. {  
  3. public:  
  4.     Handle(T *p = 0):ptr(p),use(new size_t(1)) {}  
  5.     Handle(const Handle &h):ptr(h.ptr),use(h.use)  
  6.     {  
  7.         ++ *use;  
  8.     }  
  9.     Handle &operator=(const Handle &rhs);  
  10.     ~Handle()  
  11.     {  
  12.         rem_ref();  
  13.     }  
  14.   
  15.     T &operator*();  
  16.     T *operator->();  
  17.     const T &operator*() const;  
  18.     const T *operator->() const;  
  19.   
  20. private:  
  21.     T *ptr;  
  22.     size_t *use;  
  23.   
  24.     void rem_ref()  
  25.     {  
  26.         if (-- *use == 0)  
  27.         {  
  28.             delete use;  
  29.             delete ptr;  
  30.         }  
  31.     }  
  32. };  


赋值操作符:

  1. template <class Type>  
  2. Handle<Type> &Handle<Type>::operator=(const Handle<Type> &rhs)  
  3. {  
  4.     ++ *rhs.use;  
  5.     rem_ref();  
  6.     ptr = rhs.ptr;  
  7.     use = rhs.use;  
  8.   
  9.     return *this;  
  10. }  

解引用操作符和成员访问操作符[+P562习题16.45]

如果Handle没有绑定到对象,则试图访问对象时将抛出一个异常:

  1. template <typename Type>  
  2. Type &Handle<Type>::operator*()  
  3. {  
  4.     if (ptr)  
  5.     {  
  6.         return *ptr;  
  7.     }  
  8.     throw std::runtime_error("dereference of unbound Handle");  
  9. }  
  10. template <typename Type>  
  11. Type *Handle<Type>::operator->()  
  12. {  
  13.     if (ptr)  
  14.     {  
  15.         return ptr;  
  16.     }  
  17.     throw std::runtime_error("access through unbound Handle");  
  18. }  
  19.   
  20. template <typename Type>  
  21. const Type &Handle<Type>::operator*() const  
  22. {  
  23.     if (ptr)  
  24.     {  
  25.         return *ptr;  
  26.     }  
  27.     throw std::runtime_error("dereference of unbound Handle");  
  28. }  
  29. template <typename Type>  
  30. const Type *Handle<Type>::operator->() const  
  31. {  
  32.     if (ptr)  
  33.     {  
  34.         return ptr;  
  35.     }  
  36.     throw std::runtime_error("access through unbound Handle");  
  37. }  

二、使用句柄

我们希望Handle类能够用于其他类的内部实现中

一个简单的示例:通过分配一个int对象,并将一个Handle对象绑定到新分配的int对象而说明Handle的行为:

  1. {  
  2.     Handle<int> hp(new int(42));  
  3.     {  
  4.         Handle<int> hp2 = hp;  
  5.         cout << *hp << " " << *hp2 << endl; //42 42  
  6.   
  7.         *hp2 = 10;  
  8.     }  
  9.     cout << *hp << endl;    //10  
  10. }  

即使是Handle的用户分配了int对象,Handle析构函数也将删除它。在外层代码末尾最后一个Handle对象超出作用域时,删除该int对象。

使用Handle对象对指针进行使用计数

可以重新实现Sales_item类,在类中使用Handle,该类的这个版本定义相同的接口,但可以通过用Handle<Item_base>对象代替Item_base指针而删除复制控制成员:

  1. class Sales_item  
  2. {  
  3. public:  
  4.     Sales_item():h() {}  
  5.     Sales_item(const Item_base &item):h(item.clone()) {}  
  6.   
  7.     const Item_base &operator*() const  
  8.     {  
  9.         return *h;  
  10.     }  
  11.     const Item_base *operator->() const  
  12.     {  
  13.         return h.operator -> ();  
  14.     }  
  15.   
  16. private:  
  17.     Handle<Item_base> h;  
  18. };  

因为Sales_item的这个版本没有指针成员,所以不需要复制控制成员Sales_item的这个版本可以安全的使用合成的复制控制成员。管理使用计数和相关Item_base对象的工作在Handle内部完成

因为接口没变,所以不需要改变使用Sales_item类的代码。如:

  1. double Basket::total() const  
  2. {  
  3.     double sum = 0.0;  
  4.     for (const_iter iter = items.begin();  
  5.             iter != items.end();  
  6.             iter = items.upper_bound(*iter))  
  7.     {  
  8.         sum += (*iter)->net_price(items.count(*iter));  
  9.     }  
  10.   
  11.     return sum;  
  12. }  

分析:

  1. sum += (*iter)->net_price(items.count(*iter));  

1(*iter)返回hh是使用计数式句柄的成员;

2)因此,(*iter)->使用句柄类的重载箭头操作符;

3)编译器计算h.operator->(),获得Handle对象保存的Item_base指针;

4)编译器对该Item_base指针解引用,并调用指针所指向对象的Item_base成员。

  1. //P564 习题16.51  
  2. class Query  
  3. {  
  4.     friend Query operator~(const Query &);  
  5.     friend Query operator|(const Query &,const Query &);  
  6.     friend Query operator&(const Query &,const Query &);  
  7. public:  
  8.     Query(const string &);  
  9.   
  10.     set<TextQuery::line_no> eval(const TextQuery &t) const  
  11.     {  
  12.         return h -> eval(t);  
  13.     }  
  14.     ostream &display(ostream &os) const  
  15.     {  
  16.         return h -> display();  
  17.     }  
  18.   
  19. private:  
  20.     Query(Query_base *query):h(query) {}  
  21.     Handle<Query_base> h;  
  22. };  
  23. /** 
  24. *其他操作与前相似,在此不再赘述 
  25. */ 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值