泛型句柄

在《句柄类与继承》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的定义,对这个句柄对象解引返回就是实际需要管理的数据成员,对这个句柄对象进行箭头操作返回的就是指向它的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值