代理类,来自于C++ 沉思录的概念
代理类,将继承和容器共用,控制内存分配和把不同类型的对象放入同一个容器中,代理类的每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象;容器中用代理对象,而不是对象本身。
一、问题的提出
如何设计一个C++容器,使它有能力包含类型不同,而彼此相关的对象
我们会想到存储对象的指针,通过继承来处理类型不同的问题,声明一个抽象基类,容器存储抽象基类指针,基类指针指向派生类对象。
问题一,一旦派生类对象没了,比如是局部变量,指针就不知道指什么东西了,可以让指针指向派生类对象的副本来解决这个问题;
问题二,一个指针与另外一个指针指向内容相同的对象,我们不可以用容器里的一个指针赋值给另外一个指针,这样两个指针会指向同一个对象,可以用 纯虚复制函数 解决这个问题,基类添加一个纯虚复制函数,派生类的复制函数返回其对象的动态副本,当基类指针指向一个派生类对象,利用指针调用复制函数,会获得该对象的动态副本。
二、代理概念的提出
有没有一种方法既能使我们避免显式地处理内存分配,又能保持基类在运行时绑定的属性,解决这个问题的关键是要用类来表示概念,在复制对象的过程中,定义一个行为和基类相似,而又潜在地表示所有派生类的对象的东西,这种类的对象叫做代理。
三、代理类的实现
代理类的成员变量,是其代理的基类类型的指针,代理类构造函数的参数为基类引用 ;代理类的对象的成员变量,或者指向空或者指向派生类对象的副本( 基类引用 ); 潜在地表示所有派生类的对象 ;
代理类拥有基类的所有操作,行为与基类相似;
Vehicle基类(抽象类),其代理类,VehicleSurrogate类
Vehicle类,必须有虚复制函数,虚析构函数
VehicleSurrogate类,有无参构造函数,参数为Vehicle对象引用的构造函数,析构函数,拷贝构造函数,= 运算符重载函数,来自类Vehicle的操作
#include <iostream> #include <exception> using namespace std; class Vehicle{ public: virtual double weight() const = 0; virtual void start() = 0; virtual Vehicle* copy() const = 0; virtual ~Vehicle() { } //... }; class RoadVehicle : public Vehicle { /* */ }; class AutoVehicle : public RoadVehicle{ public: double weight()const{ std:: cout << "AutoVehicle weight() ";} void start() { std:: cout << "AutoVehicle start() "; } Vehicle* copy() const { return new AutoVehicle(*this); } }; class Truck : public RoadVehicle{ public: double weight()const{ std:: cout << "Truck weight() ";} void start() { std:: cout << "Truck start() "; } Vehicle* copy() const { return new Truck(*this); } }; //class Aircraft : public Vehicle { /* */ }; //class Helicopter : public Aircraft { /* */ }; class VehicleSurrogate{ public: VehicleSurrogate(); VehicleSurrogate(const Vehicle&); ~VehicleSurrogate(); VehicleSurrogate(const VehicleSurrogate&); VehicleSurrogate& operator = (const VehicleSurrogate&); //来自类Vehicle的操作(不是虚函数) double weight() const; void start(); //... private: Vehicle* vp; }; VehicleSurrogate::VehicleSurrogate(): vp(0) { } //Vehicle类继承层次的对象的动态对象副本 VehicleSurrogate::VehicleSurrogate(const Vehicle& v): vp(v.copy()) { } VehicleSurrogate::~VehicleSurrogate() { delete vp; } VehicleSurrogate& VehicleSurrogate::operator = (const VehicleSurrogate& v) { if(this != &v){ delete vp; vp = ( v.vp ? v.vp->copy() : 0 ) ; } return *this; } VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v): vp( v.vp ? v.vp->copy() : 0 ) { } double VehicleSurrogate::weight() const{ if(vp == 0) throw "empty VehicleSurrogate.weight()"; return vp->weight(); } void VehicleSurrogate::start(){ if(vp == 0) throw "empty VehicleSurrogate.start()"; vp->start(); } int main() { int num_vehicles = 0; VehicleSurrogate parking_lot[1000]; try{ parking_lot[0].weight(); }catch(const char* e){ cout << e << endl; } Truck t; parking_lot[num_vehicles++] = t; parking_lot[0].weight(); parking_lot[0].start(); AutoVehicle a; parking_lot[num_vehicles++] = a; parking_lot[1].weight(); parking_lot[1].start(); return 0; }
Truck t; parking_lot[num_vehicles++] = t; //等同于 parking_lot[num_vehicles++] = VehicleSurrogate(t); //构造VehicleSurrogate对象,前者是隐式调用,后者是显式调用
四、总结
总之,代理类是对基类的指针进行包装,构造函数通过基类引用,构造代理类对象,构造过程中派生类的对象调用虚复制函数对对象进行动态复制,代理类对象通过基类指针与派生类对象的副本发生联系,最终代理类对象与派生类对象的副本产生一对一的关系;代理类的行为(代理类成员函数)通过基类指针调用基类虚函数实现多态,与基类类似。
五、代理使用场合
1、远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实;
2、虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象;
3、安全代理,用来控制真实对象访问时的权限;
4、智能指引,是指当调用真实对象时,代理处理另外一些事