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

来源:互联网 发布:花生壳赠送的域名在哪 编辑:程序博客网 时间:2024/04/29 11:47

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

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

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();}

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

0 0
原创粉丝点击