《C++沉思录》读书笔记之代理类

当我们使用容器(或者数组)保存因继承关系而相互关联的对象时,就会遇到问题,因为对象不是多态的。

例如有下列表示交通工具的类

class Vehicle{
	public:
		virtual double weight() const=0;
		virtual void start()=0;
	};
	
class RoadVehicle: public Vehicle{/*.....*/};
class AutoVehicle: public RoadVehicle{/*.....*/};
class Aircraft: public Vehicle{/*.....*/};
class Helicopter: public Aircraft{/*.....*/};

这些类都是Vehicle的派生类。在实际应用中,我们可能需要一个容器或数组来存放这些类。例如:

Vehicle parking_lot[1000]来存放这些类。

因为基类Vehicle里面有纯虚函数,是抽象类,不可以被实例化,这样声明是错误的。但是假设声明正确,又会产生什么样的效果呢?

Parking_lot数组存放的是Vehicle基类对象,将派生类对象赋值给基类对象时,派生类对象将被切割掉一部分(切割掉派生类的部分),转换成基类。

 

考虑到基类指针可以指向派生类,于是有了下面一种解决方案。

提供一个间接层

数组存放的不是对象,而是指针。

Vehicle *parking_lot[1000]声明指针数组。

当赋值时

AutoVehicle  x;

parking_lot[num++]=&x;

这样看起来解决了问题,但是又带来了新的问题。

首先x是一个局部对象,存储在栈上,出了定义域范围会自动释放,释放后parking_lot指向什么东西我们并不知道。

那么这样变通,parking_lot不指向x,而是创建一个副本,然后指向这个副本

parking_lot[num++]=new AutoVehicle(x);

当我们释放parking_lot数组时,释放其指向对象的副本。

这样做增加了内存开销。但是还有一个前提,就是要知道对象x的类型。假如并不知道x的类型,例如把parking_lot[a]赋值给parking_lot[b],由于不知道parking_lot[a]的类型,无法创建新的副本。

于是就有了虚复制函数。

虚复制函数

虚复制函数就是复制编译时类型位置的对象。用一个虚函数来实现

class Vehicle{
	public:
		virtual double weight() const=0;
		virtual void start()=0;
		virtual Vehicle* copy()const=0;
	};

在派生类中实现

class RoadVehicle: public Vehicle{
	/*.....*/
	Vehicle *copy()const
	{
		return new RoadVehicle(*this);
	}
	};

对于虚函数,在派生类中返回类型必须与基类的返回类型完全匹配,但是有个例外,就是虚函数基类实例返回类类型的引用或指针,则该虚函数的派生类实例可以返回基类实例返回类型的派生类(指针或者引用)。

现在已经有了运行时复制对象的方法,那么现在可不可以找到一种方法,既可以避免动态分配内存,又能保持多态的属性呢?那么就是使用代理类。

代理类

代理类是定义一个类,来管理基类指针。它的行为和基类指针类似,它可以管理基类以及基类派生的对象。
它的成员变量为基类指针。成员函数为构造函数、析构函数、重载=运算符、基类里面的虚函数。

class VehicleSurrogate{
	public:
		VehicleSurrogate();
		VehicleSurrogate(const Vehicle&);
		VehicleSurrogate(const VehicleSurrogate&);
		VehicleSurrogate& operator=(const VehicleSurrogate&);
		double weight() const;
		void start();
		~VehicleSurrogate();
	private:
		Vehicle *vp;	
	};

默认构造函数是为了创建空的代理

VehicleSurrogate::VehicleSurrogate():vp(0){}
VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()){}
//在调用copy函数之前,要先检查vp是不是空指针。
VehicleSurrogate::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);//检查vp是不是空指针
		}
		return *this;
}

之后就是为每个虚函数在代理类中创建一个方法。

double VehicleSurrogate::weight()const
{
	if(vp==0)
		throw "enpty VehicleSurrogate.weight()";
		return vp->weight();
}
void VehicleSurrogate::start()
{
	if(vp==0)
		throw "enpty VehicleSurrogate.start()";
		return vp->weight();
}

代理类为每个管理的对象创建了副本,浪费了内存。一种可以共享内存存储对象的方法是智能指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值