泛型句柄

来源:互联网 发布:max更新数据失败 编辑:程序博客网 时间:2024/06/08 10:57

在《句柄类与继承》http://blog.csdn.net/thefutureisour/article/details/7746582中,我们介绍过句柄。句柄其实就是一个复杂一点的智能指针,不仅在创建、复制、赋值,删除对象时,能够通过引用计数管理指针,而且通过动态绑定,实现了多态:既可以管理基类,可以管理派生类。但是它有一个小的缺陷,因为当时我们并没有介绍模板,所以这个句柄能管理的类型是固定的。如果我们新建立了一套继承派生体系,那么就要依葫芦画瓢的重新写一套句柄来管理它们。当我们介绍完模板之后,我们可以写一个“泛型句柄”类,当我们新建立了继承派生体系时,只需要把这个句柄加入到这个体系中就可以了。
先看看如果写这个类:

#ifndef HANDLE_H#define HANDLE_Htemplate <class T> class Handle{public://构造函数:空指针Handle(T *p = 0):ptr(p),use(new size_t(1)){}//重载解引和箭头操作符T& operator*();T* operator->();const T& operator*()const;const T* operator->()const;//复制构造函数Handle(const Handle& h):ptr(h.ptr),use(h.use){++*use;}//重载赋值操作符Handle& operator=(const Handle&);//析构函数~Handle(){rem_ref();};private://共享的对象T *ptr;//引用计数size_t *use;//删除指针的具体函数void rem_ref(){if(--*use == 0){delete ptr;delete use;}}};template<class T>inline Handle<T>& Handle<T>::operator=(const Handle &rhs){//右操作数引用计数+1++*rhs.use;//删除左操作数rem_ref();//具体对象的赋值ptr = rhs.ptr;use = rhs.use;return *this;}template <class T> inline T& Handle<T>::operator*(){if(ptr)return *ptr;//空指针时抛出异常throw std::runtime_error("dereference of unbound Handle");}template <class T> inline T* Handle<T>::operator->(){if(ptr)return ptr;//空指针时抛出异常throw std::runtime_error("access through unbound Handle");}template <class T> inline const T& Handle<T>::operator*()const{if(ptr)return *ptr;throw std::runtime_error("dereference of unbound Handle");}template <class T> inline const T* Handle<T>::operator->()const{if(ptr)return ptr;throw std::runtime_error("access through unbound Handle");}#endif

稍微啰嗦两句:这个类模板的数据成员有两个:指向某个实际需要管理的类型的数据的指针以及它的引用计数。它定义了复制控制函数以及解引、箭头操作符。其中解引操作符返回的是实际需要管理的数据,而箭头操作符返回的是这个指针。

为了简单起见,我们用一个int型来说明这个指针是如何工作的:

int main(){Handle<int> hp(new int(42));//新的作用域:作用域结束后会删除局部对象{Handle<int> hp2 = hp;cout<<*hp<<" "<<*hp2<<endl;*hp2 = 10;}cout<<*hp<<endl;return 0;}

这个程序值得注意的地方在于人为的使用{}规定了一个作用域。第一次打印时,*hp和*hp2的值为42,*hp2赋值为10以后,*hp2被释放掉了,然后打印*hp时,由于这个两个指针指向同一个数,所以打印结果为10。
下面我们言归正传,看看如果把这个泛型句柄加入到《句柄类与继承》的Sales_item类中:


include "Item.h"#include "Handle.h"class Sales_item{public://默认构造函数//指针置0,不与任何对象关联,计数器初始化为1Sales_item():h(){}//接受Item_base对象的构造函数Sales_item(const Item_base &item):h(item.clone()){}//重载成员访问操作符const Item_base *operator->()const{return h.operator->();}//重载解引操符const Item_base &operator*()const{return *h;}private:Handle<Item_base> h;};


由于我们并没有改变这个类的接口,所以其他的程序都不用修改(这里也没有贴出来)。我们只看看修改了什么:
首先由于我们定义了泛型句柄,所以这个类原来的指针和引用计数就不需要了,而换成了一个Handle类的实例。对应的,还要修改它的构造函数与复制控制部分。其中删除指针的操作由泛型句柄完成,这里也不需要了。
对于重载解引和箭头操作符,我们只要搞清楚我们自己想要的结果是什么,就能理解了:在原来的版本中,Sales_item对象的的解引将会或得Item_base 类的对象,箭头操作符返回指向Item_base 类的指针;因此,而在新的版本中,由于数据成员Handle<Item_base> h;的存在,我们的解引操作符返回的是*h,而箭头操作符返回的是h.operator->()。因为根据泛型句柄Handle的定义,对这个句柄对象解引返回就是实际需要管理的数据成员,对这个句柄对象进行箭头操作返回的就是指向它的指针。

 

原创粉丝点击