在《句柄类与继承》http://blog.youkuaiyun.com/thefutureisour/article/details/7746582中,我们介绍过句柄。句柄其实就是一个复杂一点的智能指针,不仅在创建、复制、赋值,删除对象时,能够通过引用计数管理指针,而且通过动态绑定,实现了多态:既可以管理基类,可以管理派生类。但是它有一个小的缺陷,因为当时我们并没有介绍模板,所以这个句柄能管理的类型是固定的。如果我们新建立了一套继承派生体系,那么就要依葫芦画瓢的重新写一套句柄来管理它们。当我们介绍完模板之后,我们可以写一个“泛型句柄”类,当我们新建立了继承派生体系时,只需要把这个句柄加入到这个体系中就可以了。
先看看如果写这个类:
#ifndef HANDLE_H
#define HANDLE_H
template <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,不与任何对象关联,计数器初始化为1
Sales_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的定义,对这个句柄对象解引返回就是实际需要管理的数据成员,对这个句柄对象进行箭头操作返回的就是指向它的指针。