在上一篇文章中,多次对象的复制工作,浪费了内存空间,同时也影响了程序的效率。在这里我们采用句柄的方法,消除不必要的复制。通过一个句柄来引用对象和一个引用计数器来控制该对象的创建与消除,这里所说的句柄是一个句柄类(Handle),其实就是一个指向对象的指针,当我们引用一次该对象的时候就将引用计数器加一,当计数器为零的时候,表示没有任何地方使用该对象,此时我们就要消除该对象,同时释放该对象所占用的空间。书中讲解的程序片断,整理如下:
class point //一个point类
{
public:
point(int x = 0, int y = 0) : xval(x), yval(y) { }
int x(void) const { return xval; }
int y(void) const { return yval; }
point &x(int xv) { xval = xv; return *this; }
point &y(int yv) { yval = yv; return *this; }
private:
int xval, yval;
};
class UPoint //通过一个类来控制对象,以及它的计数器
{
friend class Handle; //将handle设置为该对象的友元,这样就可以使用该类的私有成员
point p;
int u;
UPoint(void) : u(1) { }
UPoint(int x, int y) : p(x, y), u(1) { }
UPoint(const point& p0) : p(p0), u(1) { }
};
class Handle
{
public:
Handle(void) : up(new UPoint) { }
Handle(int x,int y) : up(new UPoint(x, y)) { }
Handle(const point& p) : up(new UPoint(p)) {}
Handle(const Handle* h) :up(h->up) { ++up->u; }
Handle& operator=(const Handle& h)
{
++h.up->u;
if (--up->u == 0)
{
delete up;
}
up = h.up;
return *this;
}
~Handle(void)
{
if (--up->u == 0)
{
delete up;
}
}
int x(void) const { return up->p.x(); }
Handle& x(int x0)
{
if (up->u != 1) //在句柄的复制过程中,不希望在修改原对象的值,所以就要new一个新的对象出来,对新对象进行操作,在这个地方,不知为什么不直接在handle的拷贝构造中new新的对象而要在这里new新的对象???
{
--up->u;
up = new UPoint(up->p);
}
up->p.x(x0);
return *this;
}
int y(void) const { return up->p.y(); };
Handle& y(int y0)
{
if (up->u != 1)
{
--up->u;
up = new UPoint(up->p);
}
up->p.y(y0);
return *this;
}
private:
UPoint *up;
};
在上面的程序中,的确避免的不必要的对象复制,只需要一个句柄我们就可以高效的使用这个对象。同时,在这段代码中还有一个问题:我们都要借助一个类(UPoint)来使用这个对象,将句柄邦定到该对象上,但是如果这个对象是未知的话,也就是说根本就不知道要将这个句柄邦定到那个对象上,这是我们也就无从定义UPoint。针对这一问题提出了新的解决方法,其主要思想是:将引用计数从数据中分离出来,把引用计数加到handle中去,也就不用往对象上邦定任何东西。