代理类和句柄类的一些认识

先看一个交通工具的类派生层次:

class Vehicle

{

public:

virtual double weight() const = 0;

virtual void start() = 0;

};


class RoadVehicle : public Vehicle

{


};

class AutoVehicle : public RoadVehicle

{


};


 

假设我们要跟踪处理一系列不同类型种类的Vehicle。实际中要用到某种容器类,这里用数组来实现。

如果直接用Vehicle parking_lot[1000];这样肯定是不行的,因为抽象类是不能被创建的。

经典的解决方案是提供一个间接层,OK就是指针

Vehicle* parking_lot[1000];

然后输入类似

AutoVehicle x = /* ... */;

Parking_lot[num_Vehicles++] = &x;

这种方式是有问题的,容器中存储的是局部变量,当超过局部变量作用域的时候,指针就失效了。

尝试用下面这种方法

Parking_lot[num_Vehicles++] = new AutoVehicle;

当我们想要让Parking_lot[p]创建一个和Parking_lot[q]一样的类型时,我们该怎么做呢,要在抽象类Vehicle中添加copy纯虚函数。

 

class Vehicle

{

public:

virtual double weight() const = 0;

virtual void start() = 0;

//复制

virtual Vehicle* copy() const = 0; 

};

派生类中要类似这样实现

Vehicle* AutoVehicle::copy() const

{

//使用系统默认的复制构造函数

return new AutoVehicle(*this);

}


清除对象要添加一个虚析构函数了。


如果用代理实现的话

class VehicleSurrogate

{

public:

VehicleSurrogate():vp(0){}

VehicleSurrogate(const Vehicle& v):vp(v.copy()){}

~VehicleSurrogate(){delete vp;}

VehicleSurrogate(const VehicleSurrogate& v)

:vp(v.vp?v.vp->copy():0){}

VehicleSurrogate& operator=(const VehicleSurrogate& v)

{

if(this != &v)

{

delete vp;

vp = (v.vp?v.vp->copy():0);

}

return *this;

}

double weight() const

{

if(!vp)

Throw “ empty”;

return vp->weight();

}

void start() const

{

if(!vp)

Throw “empty”;

vp->start();

}

private:

Vehicle* vp;

};


此时就不需要指针了

VehicleSurrogate parking_lot[1000];

AutoVehicle x;

Parking_lot[num_vehicles++] = x;

//最后一行相当于 = VehicleSurrogate(x);


 

关于内存的管理直接被封装到代理类中。

如果想避免复制所代理的对象,就要考虑用到句柄类(或者称为智能指针)了。

句柄类主要是避免复制对象,尤其对象是一种不能轻易被复制的资源时,例如文件。

两种实现方式,一种是数据和引用计数绑定在一起,另外一种是分开的。

先说第一种:

//数据类

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 _x){xval = _x;}

point& y(int _y){yval = _y;}

private:

int xval,yval;

};

//引用计数

class Handle;

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)){}

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(){if (!--up->u) delete up;}

int x() const{return up->p.x();}

int y() const{return up->p.y();}

Handle& x(int _x)

{

//指针语义

up->p.x(_x); 

return *this;

//值语义(保证要修改的对象不能被其他句柄引用,需要查看引用计数,如果不为1,就需要复制了)

if (up->u != 1)

{

--up->u;

up = new UPoint(up->p);

}

up->p.x(_x);

return *this;

}

Handle& y(int _y)

{

//指针语义

up->p.y(_y);

return *this;

//值语义(

if (up->u != 1)

{

//只有在必要的时候,才进行复制,这就是写时复制

--up->u;

up = new UPoint(up->p);

}

up->p.x(_x);

return *this;

}


private:

UPoint *up;


};


Handle h(3,4);

Handle h2 = h;

h2.x(5);

Int n = h.x();


 

此时n的值是3还是5呢?

低层是h2和h都指向同一数据(3,4),如果是指针语义,那n的值就是5了,如果是值语义,保证修改h2的值不会影响到h则仍是3。

第二种则是计数和数据分离(也就是用另外一个类专门管理引用计数,而句柄类中则同时存在数据和引用计数类),在这里第二种就不实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值