zoukankan      html  css  js  c++  java
  • 《C++沉思录》摘引——代理类(Surrogate)

    问题引入

    假设有一个表示不同种类的交通工具的类派生层次:

     1 class Vehicle {}
     2 {
     3 public:
     4     virtual void start() = 0;
     5     // ...
     6 };
     7 class RoadVehicle : public Vehicle {};
     8 class AutoVehicle : public RoadVehicle {};
     9 class Aircraft : public Vehicle {};
    10 class Helicopter : public Aircraft {};

     其中,Vehicle是一个抽象基类。在实际中,我们可能会使用某种容器类,比如数组:

    1 Vehicle parking_lot[10];

     上述定义没有产生预期的效果,为什么?由于Vehicle本身不会有对象,也就不可能有其对象数组了。

    如果我们去掉类Vehicle的所有纯虚函数,写出类似于下面的语句,会有什么效果呢?

    1 Automobile x = /* ... */;
    2 parking_lot[i] = x;

     答案是:把x赋给parking_lot的元素,会把x转换成一个Vehicle对象,同时会丢失所有在Vehicle类中没有的成员。该赋值语句还会把这个剪裁了的对象复制到parking_lot数组中。

    这样,我们只能说parking_lot是Vehicles的集合,而不是所有继承自Vehicle的对象的集合。

    使用代理类

     有没有一种方法既能让我们避免显示地处理内存分配,又能保持类Vehicle运行时绑定的属性呢?

    有!方法是:定义一个行为和Vehicle对象类似,而又潜在地表示了所有继承自Vehicle类的对象的东西。我们把这种类的对象叫做代理(surrogate)。

     1 class VehicleSurrogate
     2 {
     3 public:
     4     VehicleSurrogate() : vp(NULL) {};
     5     VehicleSurrogate(const Vehicle& v) : vp(v.copy()) {};
     6     ~VehicleSurrogate() {};
     7     VehicleSurrogate(const VehicleSurrogate& v) : vp(v.vp ? v.vp->copy() : NULL) {}; //v.vp非零检测
     8     VehicleSurrogate& operator=(const VehicleSurrogate& v) 
     9     {
    10         if (this != &v) // 确保没有将代理赋值给它自身
    11         {
    12             delete vp;
    13             vp = (v.vp ? v.vp->copy() : NULL);
    14         }
    15 
    16         return *this;
    17     };
    18 
    19     //来自类Vehicle的操作
    20     void start()
    21     {
    22         if (vp == 0)
    23             throw "empty VehicleSurrogate.start()";
    24 
    25         vp->start();
    26     };
    27 
    28 private:
    29     Vehicle* vp;
    30 };

     我们再定义两个Vehicle的子类Car和Truck:

     1 class Vehicle
     2 {
     3 public:
     4     virtual void start() = 0;
     5     virtual Vehicle* copy() const = 0//虚拷贝
     6     virtual ~Vehicle() {}
     7 };
     8 
     9 class RoadVehicle : public Vehicle
    10 {
    11 public:
    12     void start() { cout<<"start road vehicle"<<endl; } ;
    13     Vehicle* copy() const { return new RoadVehicle(*this); };
    14     ~RoadVehicle() {};
    15 };
    16 
    17 class Truck : public RoadVehicle
    18 {
    19 public:
    20     void start() { cout<<"start truck"<<endl; } ;
    21     Vehicle* copy() const { return new Truck(*this); };
    22     ~Truck() {};
    23 };
    24 
    25 class Car : public RoadVehicle
    26 {
    27 public:
    28     void start() { cout<<"start car"<<endl; } ;
    29     Vehicle* copy() const { return new Car(*this); };
    30     ~Car() {};
    31 };

    那么我们就能够实现继承自Vehicle的类的对象的容器:

     1 int _tmain(int argc, _TCHAR* argv[])
     2 {
     3     VehicleSurrogate parking_lot[10];
     4     Truck x;
     5     Car y;
     6     for (int i=0; i<10; i++)
     7     {
     8         // 相当于parking_lot[i] = VehicleSurrogate(x);
     9         // 创建了一个关于对象x的副本,并将VehicleSurrogate对象绑定到该副本,
    10         // 然后将这个对象赋值给parking_lot的一个元素。当最后销毁parking_lot数组时,
    11         // 所有这些副本也将被消除
    12         if (i&1)
    13             parking_lot[i] = x; //VehicleSurrogate(x);
    14         else
    15             parking_lot[i] = y; //VehicleSurrogate(y);
    16 
    17         parking_lot[i].start();
    18     }
    19 
    20     return 0;
    21 }

    结果为:

    start car

    start truck

    start car

    start truck

    start car

    start truck

    start car

    start truck

    start car

    start truck

    每个Vehicle代理都代表某个继承自Vehicle类的对象。只要该代理关联着这个对象,该对象就肯定存在。

    因此,拷贝代理就会拷贝相应的对象,而给代理赋新值也会先删除旧对象,再拷贝新对象(改变代理类实际关联的那个对象的类型)。

    所幸的是,我们在类Vehicle中已有虚拷贝函数copy来完成这些拷贝工作。

    总结

    将继承和容器共用,迫使我们要处理两个问题:控制内存分配和把不同类型的对象放入同一容器中。采用基础的C++技术,用类来表示概念,我们可以同时兼顾这两个问题。我们提出一个名叫代理类的类,这个类的每一个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象。通过在容器中用代理对象而不是对象本身的方式,解决了我们的问题。

    声明

    本文版权归《C++沉思录》原书作者所有!

  • 相关阅读:
    Java基础--线程创建方式
    Java基础--static关键字
    Java基础--异常处理
    mybatis的#{}和${}的区别以及order by注入问题
    前后端分离结构中使用shiro进行权限控制
    Java FTP下载文件
    10个经典智力推理题!据说答对7道,智力在140!
    Java面试题总结之数据结构、算法和计算机基础(刘小牛和丝音的爱情故事1)
    Java面试题总结之JDBC 和Hibernate
    Java面试题总结之数据库与SQL语句
  • 原文地址:https://www.cnblogs.com/burellow/p/2088080.html
Copyright © 2011-2022 走看看