句柄类(一)
在代理类的帮助下,我们已经可以实现在一个容器里存储一个类层次里所有类型的对象,但是代理有一个很明显的缺点,就是需要复制对象,当一个对象非常大或者是一种不能轻易复制的资源的时候,这个实现遇到了很大的困难,于是我们有了句柄(handle)类这个技术。
我们有这么一个类
class point { public: point():x_cdt(0),y_cdt(0){} point(int x,int y):x_cdt(x),y_cdt(y){} int get_x() { return x_cdt;} int get_y() { return y_cdt;} point& set_x(int x)//返回值是point&是为了实现这样的表达
{ //p.set_x(2).set_y(4);
x_cdt = x; return *this; } point& set_y(int y) { y_cdt = y; return *this; }
private: int x_cdt,y_cdt;
};
|
这个类有一切他应该有的成员函数,然后我们希望有这么一个handle能够替代实现他的所有功能,而又保证不需要复制这个对象,我们能想到的第一个方法当然是得到这个对象的指针但是这样我们就又把内存的分配暴露到了用户面前,可以随意的得到底层的指针。这个handle第一次是写成这样的:
class handle { public: handle(); handle(int x,int y); handle(const point&); handle(const handle&); handle& operator=(const handle&); ~handle(); private: point* operator->(); }
|
我们暂时没有把set_x()这样的成员先假如到这里,为了简便。当我们写完之后,我们又遇到了时刻困扰我们的问题,就是对象的删除,既然我们允许把一个对象绑定到多个handle上,当其中的一个handle删除的时候,这个对象会怎么样呢?这个问题是要解决的。这里,我们呢引入了引用计数的概念,很显然,我们不能把这个引用计数放到我们的handle类中,因为一旦我们这么做了,当我们用一个handle绑定到一个对象的时候,他必须知道其他所有绑定到这个对象的handle的引用计数,然后更新这个计数,这非常困难。那么如果我们把这个引用计数放到对象本身呢?稍微思考一下就知道也是不合理的,这样做意味着我们每次都要改写对象。所以,综上考虑,我们决定写一个新的类来存放这个引用计数,这个类是完全为了技术的实现设计的,所以我们把他的所有成员设置成private,而让handle成为他的友元类,具体是这样的:
class count_point { friend class handle; point p; int count; count_point(); count_point(int x,int y); count_point(const count_point&);
};
|
有了这个引用计数,我们就可以让我们之前的一个小忧虑变得合理点,就是我们在handle中有一个 point* operator->();
现在我们这样改写这个handle类:
class handle { public: handle(); handle(int x,int y); handle(const point&); handle(const handle&); handle& operator=(const handle&); ~handle(); private: count_point* ptr; };
|
好了,至此,框架已经行程,我们来实现句柄。
class count_point { friend class handle; point p; int count; count_point():count(1){} count_point(int x,int y):p(x,y),count(1){} count_point(const point&h):p(h),count(1){} };
|
然后是handle类:
class handle { public: handle():ptr(new count_point){} handle(int x,int y):ptr(new count_point(x,y)){} handle(const point&h):ptr(new count_point(h)){} handle(const handle&h):ptr(h.ptr){++ptr->count;} handle& operator=(const handle&h) { ++h.ptr->count; if(--ptr->count == 0) delete ptr; ptr = h.ptr; return *this; } ~handle() { if(--ptr->count == 0) delete ptr; } private: count_point* ptr; };
|
最后,我们还有一个小任务,就是实现handle中的set_x()和set_y()成员,注意当初我们为了看上去简便暂时没有把他们添加到handle中,事实上他们是应该被包含的,我们来定义一个
handle::set_x(int x) { ptr->p.set_x(x); return *this; }
|
不得不提醒,这里涉及到指针语义和值语义的问题,我们这样做,是选择了指针语义,这样做的后果就是,你改动一个handle绑定的对象将使所有与这个对象绑定的handle发生一样的变化,这样可以避免复制。