代理類的引入
為什麼需要代理,自然是用戶端與伺服器端不能正常溝通,需要第三方來進行溝通,這第三方自然成了代理,準確來說,應該是伺服器端的代理,用來實現的代理功能的類就叫代理類。
那麼什麼時候用戶端與伺服器端不能正常溝通呢?《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++沉思錄》第五章 代理類