zoukankan      html  css  js  c++  java
  • C++之代理类笔记

    代理类的引入

      为什么需要代理,自然是客户端与服务器端不能正常沟通,需要第三方来进行沟通,这第三方自然成了代理,准确来说,应该是服务器端的代理,用来实现的代理功能的类就叫代理类。

      那么什么时候客户端与服务器端不能正常沟通呢?《C++沉思录》有言:C++容器通常只能包含一种类型的对象。那么我们怎样才能设计一个C++容器,使它有能力包含类型不同而彼此相关的对象呢?首先想到的是容器里存储的是不是对象本身,而是指向对象的指针。这虽然看起来好像解决了问题,但存在两个缺点:一是存储的是指向对象的指针,不是对象本身,这样当对象被析构而指针没有被delete掉时,成了野指针。二是存储指针增加了内存分配的额外负担。基于此,我们通过定义名为代理(surrogate)的对象来解决上述问题。

    问题

      举例说明:

      假设存在下面一个表示不同各类的交通工具的派生层次:

    class Vehicle
    {
    public:
        virtual double weight()const =0;
        virtual void start() = 0;
    };
    
    class RoadVehicle:public Vehicle
    {};
    
    class AutoVehicle:public RoadVehicl
    {};
    
    class Aircrate:public Vehicle
    {};
    
    class Helicopter:public Aircraft
    {};

      假如要跟踪处理一系列不同种类的Vehicle,定义如下:Vehicle parking_lot[1000]。这样没有达到预期效果,因为Vehicle是虚基类,不可能存在对象,更不可能存在其对象数组。

    经典解决方案

      既然上面不能存在对象本身,所以提供一个间接层,最易的就是存储指向对象的指针,即Vehicle *parking_lot[1000],但上面已说到这样有两个缺陷,其中最大的缺陷是指针容易成野指针。因为成为野指针的原因是指向的原对象被析构掉了,针对此可以变通下,放入数组parking_lot的不是指向原对象的指针,而是指向它们的副本的指针,如:

    Helicopter x;
    
    parking_lot[num_vehicles++] = new Helicopter(x); 

    且采用一个约定:当释放parking_lot时,也释放其中所指向的全部对象。

      存储的是指向对象副本的指针增加了动态内存管理的负担。

    引入代理类解决方案

      代理类与原类Vehicle的关系:1.行为和原类Vehicle相似;2.潜在地表示了所有继承自虚基类Vehicle的对象。

      要潜在地表示了所有继承自虚基类Vehicle的对象,在C++中处理未知类型的对象的方法使用是使用虚函数。由于要复制任何类型的Vehicle,所以在Vehicle类中增加一个合适的虚函数:

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

      比如Helicopter的copy函数为:

    Vehicle* Helicopter::copy()const
    {
        return new Helicopter(*this);
    }

      现在定义代理类:

    
    

    class VehicleSurrogate
    {
    public:
      VehicleSurrogate();
      VehicleSurrogate(const Vehicle&);
      VehicleSurrogate(const VehicleSurrogate&);
      ~VehicleSurrogate();
      VehicleSurrogate& operator=(const VehicleSurrogate&);

    
    

      double weight()const;
      void start();

    private:

      Vehicle *vp;

    };

      代理类中定义构造函数VehicleSurrogate(const Vehicle&)来为任意继承自Vehicle的类的对象创建代理。代理类还有一个缺省构造函数,所以能够创建VehicleSurrogate对象的数组。然而缺省构造函数带来了问题,如何规定VehicleSurrogate的缺省操作?它所指向的对象的类型是什么?不可能是Vehicle,因为Vehicle是个抽象基类,根本没有Vehicle对象。所以引入定义行为

    似于零指针的空代理(empty surrogate)的概念,能够创建、销毁和复制这样的代理,但是进行其他操作就视为出错。

      下面是代理类成员函数的定义:

    VehicleSurrogate::VehicleSurrogate():vp(0){}
    
    VehicleSurrogate::VehicleSurrogate(const Vehicle& v):vp(v.copy()){}
    
    VehicleSurrogate::VehicleSurrogate(const VehicleSurrogate& v):vp(v.vp?v.vp->copy():0){}
    
    VehicleSurrogate& VehicleSurrogate::operator =(const VehicleSurrogate& v)
    {
        if(this != &v)
        {
            delete vp;
            vp = (v.vp? v.vp->copy() : 0);
        }
        return *this;
    }
    
    VehicleSurrogate::~VehicleSurrogate()
    {
        delete vp;
    }
    
    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();
    }

      定义为了代理类,就可以定义Parking_lot[1000]了:

    VehicleSurrogate parking_lot[1000];
    Helicopter x;
    Parking_lot[num_vehicle++] = x;

      最后一条语句等价为:

    Parking_lot[num_vehicle++] = VehicleSurrogate(x);

    小结

      将继承和容器共用,迫使要处理两个问题:1.控制内存分配;2.把不同类型的对象放入同一个容器中。

      代理类的每个对象都代表另一个对象,该对象可以是位于一个完整继承层次中的任何类的对象。通过在容器中用代理对象而不是对象本身。

    Reference

      1.《C++沉思录》第五章 代理类

  • 相关阅读:
    Linq-单条数据删除
    斐波那契额数列及青蛙跳台阶问题
    旋转数组的最小数字
    扑克牌的顺子
    qsort(),sort()排序函数
    从尾到头打印链表
    查找链表中倒数第k个结点
    左旋转字符串
    数组前半部分和后半部分有序的全排序
    二元树中和为某一值的所有路径
  • 原文地址:https://www.cnblogs.com/danshui/p/2614302.html
Copyright © 2011-2022 走看看