将不同类型的对象放进同一个容器

目标:不同类型的对象,放进同一个容器。(下文以数组表示容器)

代理类

问题:

现有不同种类的交通工具类派生层次,如下:

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

class Aircraft : public Vehicle {

};

class Automobile : public Vehicle {

};

我们想对一系列不同各类的Vehicle进行管理:

Vehicle parking_lot[1000];  

不足:

  • 抽象类不可实例化
  • 若基类不是抽象类,类型转换会带来问题

经典解决方法:容器中存储基类指针

Vehicle* parking_lot[1000]; 
Automobile x;

parking_lot[0] = &x;

不足:

  • 内存管理的负担
  • 若容器中的指针在其它地方被释放,会造成大量野指针

经典解决方法的改进:容器中的指针指向 原对象的副本

Vehicle* parking_lot[1000];
Automobile x;

parking_lot[0] = new Automobile(x);

不足:

  • 动态内存管理的负担
  • 需要知道对象x的静态类型

再次改进:虚复制函数

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

class Aircraft : public Vehicle {
public:
    Vehicle* copy() const {
        return new Aircraft(*this);
    }
};

class Automobile : public Vehicle {
public:
    Vehicle* copy() const {
        return new Automobile(*this);
    }
};

直接调用copy(),不需要知道x的静态类型

Vehicle* parking_lot[1000];
Automobile x;

parking_lot[0] = x.copy();

引入代理类

我们引入代理类来避免显式的内存分配。

一个Vehicle代理代表着一个继承自Vehicle的对象,只要该代理关联着该对象,该对象就一定存在。因此,复制该代理就会复制该对象。给代理赋新值也会先删除旧对象,再复制新对象。

class VehicleSurrogate {
public:

       VehicleSurrogate() : vp(0) {} // 注意这个缺省构造,我们需要定义一个“空代理”的概念,类似零指针

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

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

       ~VehicleSurrogate() {
             delete vp;
       }

       double weight() const {
             if (vp == 0)
                    throw "vp == 0";
             vp->weight();
       }

       void start() {
             if (vp == 0)
                    throw "vp == 0";
             vp->start();
       }

private:
       Vehicle* vp;
};

需要注意的是,调用vp的成员函数前需要对vp进行判空。

现在我们管理一系列的Vehicle就方便多了:

VehicleSurrogate parking_lot[10];
Automobile x;

parking_lot[0] = x;

最后一行会调用VehicleSurrogate(const Vehicle& v)完成x从Automobile到VehicleSurrogate的类型转换,而且会创建x的副本。
我们不需要考虑数组中的副本释放问题,VehicleSurrogate的析构函数会帮我们做好。

思考:如何避免代理类中的大量复制操作?

喜欢()
评论 (0)