c++代理类

问题

怎样在容器中包含类型不同而彼此相关的对象呢?
可以在容器存储指针,通过继承来处理不同的问题,但是增加了内存分配的额外负担。
通过代理类可以解决该问题,代理运行起来和所代表的对象基本相同。但是允许将整个派生层次压缩在一个对象类型中,完成一个容器包含类型不同对象的目的。

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

class RoadVehicle : public Vehicle {...}
class AutoVehicle : public RoadVehicle {...};

使用某种容器(例如数组)来处理不同种类的Vehicle。

    Vehicle parking_lot[1000];
    AutoVehicle x;
    parking_lot[num_vehicles++] = x;

上述代码有两个问题:

  1. Vehicle是虚基类(抽象基类),本身不会有对象,只有派生出来的类才能够实例化。对象不存在,对象数组也就不存在。
  2. 即使存在对象,x转换成Vehicle也会出现对象被裁剪,丢失所有Vehicle类中没有的成员。这样容器是Vehicle对象的集合,而不是所有继承自Vehicle的对象的集合。

解决方案一

提供一个间接层,即容器中存储对象指针。

    Vehicle* parking_lot[1000]; // 指针数组
    AutoVehicle x;
    parking_lot[num_vehicles++] = &x;

解决了迫切的问题,但带来了两个问题:

  1. x是临时变量,一旦销毁,parking_lot就不知道指向什么东西了;
    Vehicle* parking_lot[1000]; // 指针数组
    AutoVehicle x;
    parking_lot[num_vehicles++] = new AutoVehicle(x);
  1. 虽然不用存储局部对象指针,但带来了动态内存管理的负担。而且只有知道放到parking_lot的静态类型之后才有作用。如果不知道类型,就只能指向新建的Vehicle。
    如果parkingcl_lot[p]和parkingcl_lot[q]指向的对象相同,下面这些语句就会出错。
    if (p != q) {
       delete parking_lot[p];
       parking_lot[p] = parking_lot[q];
    }

    // 或

    if (p != q) {
       delete parking_lot[p];
       parking_lot[p] = new Vehicle(parking_lot(q); 
       // 回到以前的问题,Vehicle对象不存在,即使存在也不是想要的
    }

解决方案二

  1. 先增加个虚复制函数copy, 复制编译时类型未知的对象:
class Vehicle {
public:
    virtual double weight() = 0 ;
    virtual void start() = 0 ;
    virtual Vehicle* copy() = 0;
    virtual ~Vehicle() {}; // 清除对象
};

class RoadVehicle : public Vehicle {
public:
    // ...
    virtual Vehicle* copy()
    {
        return new RoadVehicle(*this);
    }
};
  1. 用类表示概念,定义一个代理类。
    定义一个和Vehicle对象相似,又潜在地表示了所有继承自Vehicle类的对象的东西,这种类的对象叫做代理。
class VehicleSurrogate {
public:
    // 默认构造能够创建VehicleSurrogate数组
    VehicleSurrogate() : vp(nullptr) {}
    // 为任意继承Vehicle类的对象创建代理 
    VehicleSurrogate(const Vehicle& v) : vp(v.copy()) {}
    ~VehicleSurrogate() { delete vp; }
    VehicleSurrogate(const VehicleSurrogate& v) : vp(v.vp ? v.vp->copy() : nullptr) {}
    VehicleSurrogate& operator=(const VehicleSurrogate& v) 
    {
        if (this != &v) {
            delete vp;
            vp = v.vp ? v.vp->copy() : nullptr;
        }
        return *this;
    }

    virtual double weight()
    {
        if (vp == nullptr) {
            throw "empty vehiclesurrogate weight";
        }
        return vp.weight();
    }

    virtual void start() 
    {
        if (vp == nullptr) {
            throw "empty vehiclesurrogate start";
        }
        return vp.start();
    }

private:
    Vehicle *vp;
};

默认构造带来了的问题是:如果Vehicle是抽象基类,如何处理VehicleSurrogate默认操作,vp指向的不可能是Vehicle对象。vp只能是空指针,即空代理的概念。这种默认操作只允许创建,销毁和复制,不能进行其他操作。

    VehicleSurrogate parking_lot[1000]; // 代理对象数组
    AutoVehicle x;
    parking_lot[num_vehicles++] = x; 
    // 等价于 parking_lot[num_vehicles++] = VehicleSurrogate(x)
    // 创建x的副本,并把VehicleSurrogate对象绑定到该副本,然后赋值给数组元素
    // 最后数组销毁,这些副本也跟着销毁。

总结

解决两个问题:控制内存分配和存储不同类型对象的容器。
采用c++基础技术(继承,多态),用类表示概念,提出代理类,每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象。通过在容器中使用代理类的对象而不是对象本身的方式,解决了我们的问题,其实也是采用了间接层思想(万能钥匙)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值