常见设计模式-代理模式(C++)

定义:为其他对象提供一种代理以控制对这个对象的访问。有四种常用的情况:(1)远程代理(2)虚代理,(3)保护代理,(4)智能引用。

适用场景

根据目的和实现方式的不同,代理模式可分为很多种,常见的有:

远程代理(Remote Proxy)
为一个位于不同地址空间的对象提供一个本地代理,对代理的方法调用会导致对远程对象的方法调用。ATM 就是一个例子,ATM 可能会持有(存在于远程服务器中的)银行信息的一个代理对象。

虚拟代理(Virtual Proxy)
使用虚拟代理,代理可以作为一个(资源消耗较大的)对象的代表。虚拟代理经常延迟对象的创建,直到需要为止。在创建对象之前(及创建对象过程中),虚拟代理也可以作为对象的代理;之后,代理将请求直接委托给 RealSubject。

保护代理(Protection Proxy)
根据访问权限,可以使用保护代理来控制对资源的访问。例如,有一个员工对象,保护代理可以允许普通员工调用对象的某些方法,管理员调用其他方法。

缓冲代理(Cache Proxy)
为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

智能引用代理(Smart Reference Proxy)

当一个对象被引用时,提供一些额外的操作(例如:将对象被调用的次数记录下来)。
考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。这是[DP]一书上的给的例子

#include<iostream>
#include<cstring>

using namespace std;

class Image
{
public:
    Image(string name):m_ImageName(name){}
    virtual void show() {}
public:
    string m_ImageName;
};

class BigImage : public Image
{
public:
    BigImage(string name):Image(name){}
    void show() { cout << "BigImage show" << endl; }
};

class BigImageProxy : public Image
{
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();
    }
public:
    BigImage * m_BigImage;
};

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;  
        }  
    }  
};  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值