C++之代理類筆記

來源:互聯網
上載者:User

代理類的引入

  為什麼需要代理,自然是用戶端與伺服器端不能正常溝通,需要第三方來進行溝通,這第三方自然成了代理,準確來說,應該是伺服器端的代理,用來實現的代理功能的類就叫代理類。

  那麼什麼時候用戶端與伺服器端不能正常溝通呢?《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++沉思錄》第五章 代理類

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.