軟件領域中的設計模式爲開發人員提供了一種使用專家設計經驗的有效途徑。設計模式中運用了面向對象編程語言的重要特性:封裝、繼承、多態,真正領悟設計模式的精髓是可能一個漫長的過程,需要大量實踐經驗的積累。最近看設計模式的書,對於每個模式,用C++寫了個小例子,加深一下理解。主要參考《大話設計模式》和《設計模式:可複用面向對象軟件的基礎》(DP)兩本書。本文介紹代理模式的實現。
[DP]上的定義:爲其他對象提供一種代理以控制對這個對象的訪問。有四種常用的情況:(1)遠程代理,(2)虛代理,(3)保護代理,(4)智能引用。本文主要介紹虛代理和智能引用兩種情況。
考慮一個可以在文檔中嵌入圖形對象的文檔編輯器。有些圖形對象的創建開銷很大。但是打開文檔必須很迅速,因此我們在打開文檔時應避免一次性創建所有開銷很大的對象。這裏就可以運用代理模式,在打開文檔時,並不打開圖形對象,而是打開圖形對象的代理以替代真實的圖形。待到真正需要打開圖形時,仍由代理負責打開。這是[DP]一書上的給的例子。下面給出代理模式的UML圖。
簡單實現如下:
- class Image
- {
- public:
- Image(string name): m_imageName(name) {}
- virtual ~Image() {}
- virtual void Show() {}
- protected:
- string m_imageName;
- };
- class BigImage: public Image
- {
- public:
- BigImage(string name):Image(name) {}
- ~BigImage() {}
- void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
- };
- class BigImageProxy: public Image
- {
- private:
- BigImage *m_bigImage;
- public:
- BigImageProxy(string name):Image(name),m_bigImage(0) {}
- ~BigImageProxy() { delete m_bigImage; }
- void Show()
- {
- if(m_bigImage == NULL)
- m_bigImage = new BigImage(m_imageName);
- m_bigImage->Show();
- }
- };
客戶調用:
- int main()
- {
- Image *image = new BigImageProxy("proxy.jpg"); //代理
- image->Show(); //需要時由代理負責打開
- delete image;
- return 0;
- }
在這個例子屬於虛代理的情況,下面給兩個智能引用的例子。一個是C++中的auto_ptr,另一個是smart_ptr。自己實現了一下。先給出auto_ptr的代碼實現:
- template<class T>
- class auto_ptr {
- public:
- explicit auto_ptr(T *p = 0): pointee(p) {}
- auto_ptr(auto_ptr<T>& rhs): pointee(rhs.release()) {}
- ~auto_ptr() { delete pointee; }
- auto_ptr<T>& operator=(auto_ptr<T>& rhs)
- {
- if (this != &rhs) reset(rhs.release());
- return *this;
- }
- T& operator*() const { return *pointee; }
- T* operator->() const { return pointee; }
- T* get() const { return pointee; }
- T* release()
- {
- T *oldPointee = pointee;
- pointee = 0;
- return oldPointee;
- }
- void reset(T *p = 0)
- {
- if (pointee != p) {
- delete pointee;
- pointee = p;
- }
- }
- private:
- T *pointee;
- };
閱讀上面的代碼,我們可以發現 auto_ptr 類就是一個代理,客戶只需操作auto_prt的對象,而不需要與被代理的指針pointee打交道。auto_ptr 的好處在於爲動態分配的對象提供異常安全。因爲它用一個對象存儲需要被自動釋放的資源,然後依靠對象的析構函數來釋放資源。這樣客戶就不需要關注資源的釋放,由auto_ptr 對象自動完成。實現中的一個關鍵就是重載瞭解引用操作符和箭頭操作符,從而使得auto_ptr的使用與真實指針類似。
我們知道C++中沒有垃圾回收機制,可以通過智能指針來彌補,下面給出智能指針的一種實現,採用了引用計數的策略。
- template <typename T>
- class smart_ptr
- {
- public:
- smart_ptr(T *p = 0): pointee(p), count(new size_t(1)) { } //初始的計數值爲1
- smart_ptr(const smart_ptr &rhs): pointee(rhs.pointee), count(rhs.count) { ++*count; } //拷貝構造函數,計數加1
- ~smart_ptr() { decr_count(); } //析構,計數減1,減到0時進行垃圾回收,即釋放空間
- smart_ptr& operator= (const smart_ptr& rhs) //重載賦值操作符
- {
- //給自身賦值也對,因爲如果自身賦值,計數器先減1,再加1,並未發生改變
- ++*count;
- decr_count();
- pointee = rhs.pointee;
- count = rhs.count;
- return *this;
- }
- //重載箭頭操作符和解引用操作符,未提供指針的檢查
- T *operator->() { return pointee; }
- const T *operator->() const { return pointee; }
- T &operator*() { return *pointee; }
- const T &operator*() const { return *pointee; }
- size_t get_refcount() { return *count; } //獲得引用計數器值
- private:
- T *pointee; //實際指針,被代理
- size_t *count; //引用計數器
- void decr_count() //計數器減1
- {
- if(--*count == 0)
- {
- delete pointee;
- delete count;
- }
- }
- };