《C++沉思录》第5章 代理类——(整理)
发布日期:2021-11-07 23:20:52 浏览次数:4 分类:技术文章

本文共 2611 字,大约阅读时间需要 8 分钟。

                                                                                                  《C++沉思录》第5章 代理类——(整理)
转自:http://hi.baidu.com/cndx100/blog/item/e009cd46e3a9650f6a63e5e4.html
2008-10-12 22:32

       将继承和容器共用.迫使我们要处理两个问题: 控制内存分配和把不同类型的对象放入同一个容器中. 我们提出一个名叫代理类的类. 这个类的每个对象都代表另一个对象. 这样使用代理对象而不是对象本身就解决我们的问题.

       代理类的每一个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象。通过在容器中用代理类而不是对象本身可以解决控制内存分配和把不同类型的对象放在同一个容器中这两个问题。

一、问题

抽象基类Vehicle包含成员
double weight() const
void start()

以下4个类从Vehicle派生,每个类另有自己的独特的成员,比如Aircraft包含成员 void fly()

RoadVehicle
AutoVehicle
Aircraft
Helicopter

设计一个容器,它有能力包含类型不同而彼此相关的对象将整个派生层次压缩在一个对象类型中

二、解决方案
方案1:数组存储基类对象
Vehicle parking_lot[1000];

潜在问题:

1. 抽象基类无法实例化
2. 即使基类不是抽象类,存储派生类对象也会造成对象被剪裁
方案2:数组存储基类指针
Vehicle* parking_lot[1000];

潜在问题:

1. 存储的对象都有各自的生存期
方案3:数组存储指向对象副本的指针
对象副本的生存期由数组决定,释放数组时也释放其中指向的全部对象

潜在问题:

带来动态内存管理的负担
放入数组的对象只能是静态类型,如果想让parking_lot[p]指向一个新建的对象,这个对象的实际类型和parking_lot[q]指向的对象相同,则无法创建出这个对象(不使用RTTI)
方案4:数组存储指向对象副本的指针,为派生层次添加虚复制函数
Virtual Vehicle* copy() const = 0; //基类
Vehicle* Aircraft::copy() const //派生类
{
    return new Aircraft(*this);
}
还必须为派生层次添加虚析构函数

潜在问题:

显式处理内存分配
方案5:代理类
定义一个行为和Vehicle对象相似,又潜在的表示所有继承自Vehicle类的对象
class VehicleSurrogate {
public:
    VehicleSurrogate(); //可以创建VehicleSurrogate对象的数组
    VehicleSurrogate(const Vehicle&); //由任意继承自Vehicle的对象创建代理
    ~Vehiclesurrogate();
    VehicleSurrogate(const VehicleSurrogate&);
    VehicleSurrogate& operator= (const VehicleSurrogate&);
private:
    Vehicle* vp;
public: //来自Vehicle的操作
    double weight() const;
    void start();
}
VehicleSurrogate::VehicleSurrogate():vp(0) {} //可以生成空代理(empty surrogate)
VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()) {}
VehicleSurrogate::~VehicleSurrogate() { delete vp; }
VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate&v):vp( v.vp?v.vp->copy():0 ) {}
VehicleSurrogate&
VehicleSurrogate::operator=(const VehicleSurrogate& v)
{
    if (this != &v) { //防止将代理复制给自身
       delete vp;
       vp = (v.vp?v.vp->copy():0);
    }
    return *this;
}

这里有3个技巧值得我们注意.

        首先,注意每次对copy的调用都是一个虚拟设用.这些调用只能是虚拟的,别无选择,因为类vehicle的对象不存在.即使是那个只接收一个const vehicle&参数的复制构造函数中,它所进行的v.copy调用也是一次虚拟调用,因为v是一个引用而不是一个普通对象.

        其次,注意前于复制构造函数和赋值操作符中的v.vp非零的检测,这个检测是必需的,否则调用v..vp->copy时就会出错.

        再次,注意对赋值操作符进行检测,确保没有将代理赋值经它自身.

//来自Vehicle的操作
double VehicleSurrogate::weight() const
{
    if(0 == vp)
    throw "empty VehicleSurrogate.weight()";
    return vp->weight();
}
void VehicleSurrogate::start()
{
    if(0 == vp)
    throw "empty VehicleSurrogate.start()";
    vp->start();
}
*空代理:行为类似于零指针,可以创建、销毁和复制这样的代理,但是其他操作被视为出错

使用代理类
VehicleSurrogate parking_lot[1000];
Aircraft x;
parking_lot[0] = x;

小结:

        将继承和容器共用,迫使我们要处理两个问题:控制内存分配和把不同类型的对象放入同一个容器中.采用基础的C++技术,用类来表示概念,我们可以同时兼顾这两个问题..做这些事情的时候,我们提出一个名叫代理类的类,这个类的每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象.通过在容器中用代理对象而不是对象本身的方式,解决了我们的问题.

转载地址:https://blog.csdn.net/zhangwu416826/article/details/7431279 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:boost::mutex vs boost::recursive_mutex
下一篇:C++ 书籍

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月04日 15时38分16秒