代理类

  代理类,来自于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;
}
View Code
    Truck t;
    parking_lot[num_vehicles++] = t; 
    //等同于 parking_lot[num_vehicles++] = VehicleSurrogate(t);
    //构造VehicleSurrogate对象,前者是隐式调用,后者是显式调用

  四、总结

  总之,代理类是对基类的指针进行包装,构造函数通过基类引用,构造代理类对象,构造过程中派生类的对象调用虚复制函数对对象进行动态复制,代理类对象通过基类指针与派生类对象的副本发生联系,最终代理类对象与派生类对象的副本产生一对一的关系;代理类的行为(代理类成员函数)通过基类指针调用基类虚函数实现多态,与基类类似。

  五、代理使用场合

  1、远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实;
  2、虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象;
  3、安全代理,用来控制真实对象访问时的权限;
  4、智能指引,是指当调用真实对象时,代理处理另外一些事

原文地址:https://www.cnblogs.com/GoldenEllipsis/p/13571156.html