目标:不同类型的对象,放进同一个容器。(下文以数组表示容器)
代理类
问题:
现有不同种类的交通工具类派生层次,如下:
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的析构函数会帮我们做好。
思考:如何避免代理类中的大量复制操作?