第五章介绍了代理类,这个类能让我们在一个容器中存储类型不同但相互关联的对象。这种方法需要为每个对象创建一个代理,并要将代理存储在容器中。创建代理将会复制所代理的对象。
如果想避免这些复制该怎么做呢?可以使用句柄类。它允许在保持代理的多态行为的同时,还可以避免进行不必要的复制。处于多态的环境中,我们可以知道对象的基类类型,但是不知道对象本身的类型或者怎么样复制这种类型的对象。如果多个指针指向同一个对象,就必须考虑要在什么时候删除对象。不能太早也不能太晚,太早删除,就会有某个仍然指向它的指针存在,再使用这个指针就会产生未定义行为。删除得太晚又会占用本来早该另作它用的空间。需要一种方法,让我们避免某些缺点的同时又能够获取指针的某些优点,尤其是在能够保持多态性的前提下避免复制对象的代价。C++的解决方法就是定义一个适当的类。由于这些类的对象通常被绑定到它们所控制的对象上,所以这些类常被称为句柄类(handle class)。假定有这样一个类:
class Point
{
private:
int xval, yval;
public:
Point() : xval(0), yval(0) { }
Point(int x, int y) : xval(x), yval(y) { }
int x() const { return xval;}
int y() const { return yval;}
Point& x(int xv)
{
xval = xv;
return *this;
}
Point& y(int yv)
{
yval = yv;
return *this;
}
};
handle 应该“控制”它所绑定的对象,也就是说handle应该创建和销毁对象。有两种方式:可以创建自己的Point对象并把它赋给一个handle去进行复制,或者可以把用于创建Point的参数传给这个handle。我们要允许这两种方法,所以想让handle类的构造函数和Point类的构造函数一样。也就是,我们想用:
Handle h0(123,456);
来创建绑定到新分配的坐标为123和456的Point的handle,而用:
Handle h(p);
创建副本,并将handle绑定到该副本。这样,handle就可以控制对副本的操作。从效果上说,handle 就是一种包含单个对象的容器。当handle绑定到Point类之后,可以对 “->”进行重载,使用operator-> 将handle的所有操作转发给相应的Point操作来执行。但是这种操作也会过于暴露Point的操作,如果想绕开operator->(),就必须为handle提供自己的x和y操作,这两个操作要么返回int,要么返回Handle&。根据上面的分析给出Handle 类的大致轮廓:
class Handle
{
public:
Handle();
Handle(int, int);
Handle(const Point &);
Handle(const Handle &);
Handle & operator=(const Handle &);
~Handle();
int x() const;
Handle & x(int );
int y() const;
Handle & y(int );
private:
// ..
};
使用句柄原因之一就是为了避免不必要的对象复制,也就是说允许多个句柄绑定到单个对象上。我们必须了解有多少个句柄绑定在同一个对象上,只有这样才能确定应当何时删除对象。而通常使用引用计数来达到这个目的。但是,这个引用计数不能是句柄的一部分,否则句柄的设计会相当麻烦。也不能让引用计数成为对象的一部分,因为那样要求我们重写已经存在的对象类。我们必须定义一个新的类来容纳一个引用计数和一个Point对象。我们成为UPoint。这个类纯粹是为了实现而设计的,所以我们把其所有成员都设置为private,并且将我们的句柄类声明为友元。我们希望能以创建Point的全部方式创建UPoint对象,所以:
class UPoint
{
friend class Handle;
Point P;
int u;
UPoint() : u(1) { } // 引用计数初始化为1
UPoint(int x , int y) : P(x, y), u(1) { }
UPoint(const Point & p0) : P(p0), u(1) { }
}
现在可以完善Handle类了
class Handle
{
private:
UPoint *up; //和间接层UPoint打交道了
public:
Handle();
Handle(int, int);
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
int x() const;
Handle& x(int);
int y() const;
Handle& y(int);
};
现在来列出全部代码:
/*
从效果上来说,handle就是一种只包含单个对象的容器,它通过允许多个handle
对象指向同一个对象来避免复制。定义句柄类,我们还需要新定义一个类来容
纳被引用类对象的引用计数和被引用类对象本身。
这里的引用计数为0时删除p的意思应该是由handle创建的p
handle的构造函数中有一种是跟p有关的,在这类构造函数中
创建了p并初始化了p的计数,当这个计数为0时删除的是由handle创建
的p,如果Point自身不实例化对象的话,这样就真的实现了删除p对象了
可见,handle需要提供hanle(const handle &) , hanle(const Point &)和hanle(int x, int y)
三类构造函数的必要性,都是为了创建Point对象,而且这样创建的Point对象可以与handle关联
*/
#include <iostream>
using namespace std;
class Point{
public:
Point() : xval(0),yval(0){};
Point(int x, int y): xval(x), yval(y){};
int x() const {return xval;};
int y() const {return yval;};
Point& x(int xv)
{
xval = xv; y
return *this;
};
Point& y(int yv)
{
yval = yv;
return *this;
};
private:
int xval, yval;
};
class UPoint{ //引用计数类
friend class Handle;
Point p;
int u; //引用计数变量
UPoint(): u(1){};
UPoint(int x, int y): p(x,y), u(1){};
UPoint(const Point& p0): p(p0),u(1){};
};
class Handle{ //句柄类
public:
Handle(): up(new UPoint){};
Handle(int x,int y): up(new UPoint(x,y)){};//按创建Point的方式构造handle,handle->UPoint->Point
Handle(const Point& p): up(new UPoint(p)){};//创建Point的副本
Handle(const Handle& h): up(h.up){ ++up->u;};//此处复制的是handle,但是底层的point对象并未复制,只是引用计数加1
Handle& operator=(const Handle& h)
{
++h.up->u; //右边的对象引用计数加1,左边的减1
if(--up->u == 0)
delete up;
up = h.up;
return *this;
};
~Handle()
{
if(--up->u == 0)
delete up;
};
int x() const{return up->p.x();};
Handle& x(int xv)
{
up->p.x(xv);
return *this;
};
int y() const{return up->p.y();};
Handle& y(int yv)
{
up->p.y(yv);
return *this;
};
int OutputU(){return up->u;}; //输出引用个数
private:
UPoint* up;
};
int main()
{
//Point *p = new Point(8,9);
Point p(8,9);
//Point p1 = p.x(88);
//Point *pp = &p;
Handle h1(1,2);
Handle h2 = h1; //此处调用的是构造函数Handle(const Handle& h)
h2.x(3).y(4); //此处的特殊写法是因为写xy函数内返回了对象
Handle h3(5,6); //此处调用Handle的赋值运算符重载函数Handle& operator=(const Handle& h)
h1 = h3;
Handle h4(p);
Handle h5(h4);
h4.x(7).y(8);
//Handle h5(p1);
//Handle h5 = h4;
cout <<"h1(" << h1.x() <<":"<< h1.y() << "):" << h1.OutputU() <<endl;
cout <<"h2(" << h2.x() <<":"<< h2.y() << "):" << h2.OutputU() <<endl;
cout <<"h3(" << h3.x() <<":"<< h3.y() << "):" << h3.OutputU() <<endl;
cout <<"h4(" << h4.x() <<":"<< h4.y() << "):" << h4.OutputU() <<endl;
cout <<"h5(" << h5.x() <<":"<< h5.y() << "):" << h5.OutputU() <<endl;
//delete pp; //不能这样,不是用new分配的空间
//cout <<"h5(" << h5.x() <<":"<< h5.y() << "):" << h5.OutputU() <<endl;
cout<<p.x()<<" "<<p.y()<<endl;
//cout<<&p1<<endl;
return 0;
}
运行结果:
原文链接: https://www.cnblogs.com/zhuyp1015/archive/2012/07/26/2610847.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/56823
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!