概述
作為C++工程師,免不了要管理記憶體,記憶體管理也是C++中的痛點,而智能指標採用引用計數的辦法很方便的幫我們管理了記憶體的使用,極大方便了我們的工作效率。而智能指標的這種用法其實就是代理模式的一種,他幫我們控制了該對象的記憶體使用量。
代理模式就是為其他對象提供一種代理來控制對這個對象的訪問。
種類和用途
Proxy模式根據種類不同,效果也不盡相同:
1、遠程(Remote)代理:為一個位於不同的地址空間的對象提供一個局域代表對象。這個不同的地址空間可以是在本機器中,也可是在另一台機器中。遠程代理又叫做大使(Ambassador)。好處是系統可以將網路的細節隱藏起來,使得用戶端不必考慮網路的存在。客戶完全可以認為被代理的對象是局域的而不是遠端,而代理對象承擔了大部份的網路通訊工作。由於客戶可能沒有意識到會啟動一個耗費時間的遠程調用,因此客戶沒有必要的思想準備。
2、虛擬(Virtual)代理:根據需要建立一個資源消耗較大的對象,使得此對象只在需要時才會被真正建立。使用虛擬代理模式的好處就是代理對象可以在必要的時候才將被代理的對象載入;代理可以對載入的過程加以必要的最佳化。當一個模組的載入十分耗費資源的情況下,虛擬代理的好處就非常明顯。
3、Copy-on-Write代理:虛擬代理的一種。把複製(複製)拖延到只有在用戶端需要時,才真正採取行動。
4、保護(Protector Access)代理:控制對一個對象的訪問,如果需要,可以給不同的使用者提供不同層級的使用許可權。保護代理的好處是它可以在已耗用時間對使用者的有關許可權進行檢查,然後在核實後決定將調用傳遞給被代理的對象。
5、Cache代理:為某一個目標操作的結果提供臨時的儲存空間,以便多個用戶端可以共用這些結果。
6、防火牆(Firewall)代理:保護目標,不讓惡意使用者接近。
7、同步化(Synchronization)代理:使幾個使用者能夠同時使用一個對象而沒有衝突。
8、智能引用(SmartReference)代理:當一個對象被引用時,提供一些額外的操作,比如將對此對象調用的次數記錄下來等。
在所有種類的代理模式中,虛擬(Virtual)代理、遠程(Remote)代理、智能引用代理(SmartReference Proxy)和保護(Protector Access)代理是最為常見的代理模式。
類圖和執行個體
代理模式所涉及的角色有:
抽象主題角色(Subject):聲明了真實主題和代理主題的共同介面,這樣一來在任何使用真實主題的地方都可以使用代理主題。
代理主題(Proxy)角色:代理主題角色內部含有對真是主題的引用,從而可以在任何時候操作真實主題對象;代理主題角色提供一個與真實主題角色相同的介面,以便可以在任何時候都可以替代真實主體;控制真實主題的應用,負責在需要的時候建立真實主題對象(和刪除真實主題對象);代理角色通常在將用戶端調用傳遞給真實的主題之前或之後,都要執行某個操作,而不是單純的將調用傳遞給真實主題對象。
真實主題角色(RealSubject)角色:定義了代理角色所代表的真實對象。
這裡給出一個C++中智能指標的例子,自己代碼重新實現了下:
// TestProxy.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <assert.h>#define KSAFE_DELETE(p) \ if (p) \ { \ delete p; \ p = NULL; \ }class KRefCount{public: KRefCount():m_nCount(0){}public: void AddRef(){m_nCount++;} int Release(){return --m_nCount;} void Reset(){m_nCount=0;}private: int m_nCount;};template <typename T>class KSmartPtr{public: KSmartPtr(void) : m_pData(NULL) { m_pReference = new KRefCount(); m_pReference->AddRef(); } KSmartPtr(T* pValue) : m_pData(pValue) { m_pReference = new KRefCount(); m_pReference->AddRef(); } KSmartPtr(const KSmartPtr<T>& sp) : m_pData(sp.m_pData) , m_pReference(sp.m_pReference) { m_pReference->AddRef(); } ~KSmartPtr(void) { if (m_pReference && m_pReference->Release() == 0) { KSAFE_DELETE(m_pData); KSAFE_DELETE(m_pReference); } } inline T& operator*() { return *m_pData; } inline T* operator->() { return m_pData; } KSmartPtr<T>& operator=(const KSmartPtr<T>& sp) { if (this != &sp) { if (m_pReference && m_pReference->Release() == 0) { KSAFE_DELETE(m_pData); KSAFE_DELETE(m_pReference); } m_pData = sp.m_pData; m_pReference = sp.m_pReference; m_pReference->AddRef(); } return *this; } KSmartPtr<T>& operator=(T* pValue) { if (m_pReference && m_pReference->Release() == 0) { KSAFE_DELETE(m_pData); KSAFE_DELETE(m_pReference); } m_pData = pValue; m_pReference = new KRefCount; m_pReference->AddRef(); return *this; } T* Get() { T* ptr = NULL; ptr = m_pData; return ptr; } void Attach(T* pObject) { if (m_pReference->Release() == 0) { KSAFE_DELETE(m_pData); KSAFE_DELETE(m_pReference); } m_pData = pObject; m_pReference = new KRefCount; m_pReference->AddRef(); } T* Detach() { T* ptr = NULL; if (m_pData) { ptr = m_pData; m_pData = NULL; m_pReference->Reset(); } return ptr; }private: KRefCount* m_pReference; T* m_pData;};
與其他模式的區別
1)適配器模式Adapter
適配器Adapter為它所適配的對象提供了一個不同的介面。相反,代理提供了與它的實體相同的介面。然而,用於訪問保護的代理可能會拒絕執行實體會執行的操作,因此,它的介面實際上可能只是實體介面的一個子集。
2) 裝飾器模式Decorator
儘管Decorator的實現部分與代理相似,但Decorator的目的不一樣。Decorator為對象添加一個或多個功能,而代理則控制對對象的訪問。
總結
在軟體系統中,加一個中介層是我們常用的解決方案,這方面Proxy模式給了我們很好的實現。
LCL_data原創於CSDN.NET【http://blog.csdn.net/lcl_data/article/details/8989420】