设计模式--委托模式C++实现

原文章地址:http://www.cnblogs.com/zplutor/archive/2011/09/17/2179756.html

【委托模式 C++实现】

我对.Net的委托模型印象很深刻,使用委托,可以快速实现观察者模式,免去写很多繁杂重复的代码。遗憾的是,C++并没有提供这样的模型,为了达到相似的目的,需要继承一个类并重写virtual方法,这种做法需要写很多代码,效率比较低下(使用过MFC的应该都能体会到)。然而,在强大的C++面前,没有什么是不可能的,已经有很多人针对这个问题进行过研究,并且实现了各种委托模型,其中最著名的就是FastDelegate,这个模型在《Member Function Pointers and the Fastest Possible C++ Delegates》中提出(原文地址:http://www.codeproject.com/KB/cpp/FastDelegate.aspx)。这个模型的特点就是“Fast”,因此不可避免地要依赖编译器的具体实现,虽然文章的最后说明该模型已在大部分的编译器上通过了测试,我仍然对此不太放心,要是哪个编译器升级后改变了实现方式,这个模型就不适合使用了。而且,由于自身水平有限以及懒惰的心理,我也不想去深究每种编译器的具体实现方式。我想要的是符合C++标准,与编译器无关的模型,而不管它是否“Fast”。经过不断的摸索,终于写出了这样的一个委托模型,下面与大家分享一下该模型的实现原理。(当然,如果你认为FastDelegate已经满足需求,而且不担心它依赖于编译器,那么完全可以忽略本文)

成员函数指针

在开始之前首先介绍一下成员函数指针,它与非成员函数指针的操作方式有很大的不同。有这么一个类:

class A{
public:

    void Func(int){
        std::cout << "I am in A" << std::endl;
    }
};

要取得Func函数的指针,必须这么做:

void (A::*pFunc)(int) = &A::Func;

::*是一个特殊的操作符,表示pFunc是一个指针,指向A的成员函数。获取成员函数的地址不能通过类对象来获取,必须像上面的那样,通过类名获取,而且要加上取地址操作符(&)。

那么如何通过成员函数指针来调用该函数呢?成员函数都有一个隐含的this参数,表示函数要操作的对象,现在我们只获取到了函数的指针,还缺少一个对象作为this参数。为了达到这个目的,需要先创建一个对象,然后通过该对象来调用成员函数指针:

A a;
(a.*pFunc)(10);
A *pa = &a;
(pa->*pFunc)(11);

第一种方式是通过对象本身来调用,第二种方式是通过对象指针来调用,两种方式的效果都是一样的。.*->*都是特殊的操作符,不必纠结于它们奇怪的样子,只要知道它们只用于调用成员函数指针就行了。

使用模板类

通过上面的介绍,我们知道了要调用一个成员函数,仅仅有成员函数指针是不够的,还需要一个指向对象的指针,所以要用一个类将两者绑到一起。由于对象的类型是无穷多的,所以这里必须使用类模板:

template <typename T>
class DelegateHandler{
public:
	DelegateHandler(T *pT, void (T::*pFunc)(int)) : m_pT(pT),
				m_pFunc(pFunc){}

	void Invoke(int value){
		(m_pT->*m_pFunc)(value);
	}

private:
	T *m_pT;
	void (T::*m_pFunc)(int);
};

可以像下面那样使用该模板:

A a;
DelegateHandler<A> dha(&a, &A::Func);
dha.Invoke(3);

B b;
DelegateHandler<B> dhb(&b, &B::Method);	//B::Method的声明与A::Func类似
dhb.Invoke(4);

到这里产生了一个问题:如果希望调用的目标是非成员函数,怎么办?上面的类模板无法调用非成员函数,不过使用模板偏特化就可以解决这个问题:

template<>
class DelegateHandler<void>{	//void代替前面的类型T,表示类模板没有参数

public:
	DelegateHandler(void(*pFunc)(int)) : m_pFunc(pFunc){}

	void Invoke(int value){
		(*m_pFunc)(value);
	}

private:
	void(*m_pFunc)(int);
};

使用方法也是一样的:

void NonmemberFunc(int param){
	std::cout << "I am in NonmemberFunc!" << std::endl;
}

DelegateHandler<void> dhf(NonmemberFunc);
dhf.Invoke(5);

使用多态

对于单目标的委托来说,使用上面的代码或许就已经足够了。但是我的目的当然不止于此,我想要的是多目标的委托。多目标委托其实就是一个容器,在这个容器里可以存放多个对象(这里的对象指的是委托对象,不是A也不是B),当调用委托的时候依次调用每个对象。容器里的对象应该都是相同的类型,这样才能够放到强类型的容器中;而且委托调用方不应该知道具体的调用目标是什么,所以这些对象也应该要隐藏具体的细节。遗憾的是,上一步中实现的类模板都不具备这些能力,DelegateHandler<A>和DelegateHandler<B>是不同的类型,不能放到同一个容器中,调用方要调用它们也必须知道调用的目标是什么类型

解决这个问题的方法就是使用多态,令所有的委托目标类都继承一个公共的接口,调用方只通过这个接口来进行调用,这样就不必知道每个目标(这里的目标还是指的委托)具体的类型。下面就是该接口的定义:

class IDelegateHandler{
public:
	virtual ~IDelegateHandler(){}
	virtual void Invoke(int) = 0;
};

然后令DelegateHandler继承该接口:

template<typename T>
class DelegateHandler : public IDelegateHandler{
public:
	DelegateHandler(T *pT, void (T::*pFunc)(int)) : m_pT(pT),
		m_pFunc(pFunc){}

	virtual void Invoke(int value) override {
		(m_pT->*m_pFunc)(value);
	}

private:
	T *m_pT;
	void (T::*m_pFunc)(int);

};

template<>
class DelegateHandler<void> : public IDelegateHandler{
public:
	DelegateHandler(void (*pFunc)(int)) : m_pFunc(pFunc){}
	virtual void Invoke(int value) override{
		(*m_pFunc)(value);
	}
private:
	void (*m_pFunc)(int);

};

现在可以将各种类型的DelegateHandler放到同一个容器中,并使用同样的方式来调用了:

A a;
B b;
DelegateHandler<A> dha(&a, &A::*Func);
DelegateHandler<B> dhb(&b, &B::*Method);
DelegateHandler<void> dhf(NonmemberFunc);

std::vector<IDelegateHandler*> handlers;
handlers.push_back(&dha);
handlers.push_back(&dhb);
handlers.push_back(&dhf);

//这里的auto等于std::vector<IDelegateHandler*>::const_iterator
for (auto itor = handlers.cbegin(); itor != handlers.cend(); ++itor){
	(*itor)->Invoke(7);
}

注意这里在Linux下编译的时候,如果利用gcc或者g++编译,必须加上 -std=c++11 ,因为 auto 、 cbegin() 、 cend() 都是C++11的新特性。

时间: 2024-10-07 06:39:19

设计模式--委托模式C++实现的相关文章

IOS常用设计模式——委托模式(IOS开发)

委托模式在之前的博客中用到了很多,比如各种复杂的Cocoa Touch框架的UI控件,几乎都用到了委托来响应控件事件或控制其他对象. 委托模式 -问题: 处理一切IOS应用都在UIApplication中完成,但是在设计过程中并不友好,它藕合度高,职责不清,难以维护,需要不断不断的重构,因此需要把看似功能很复杂的类分解或者分派成功能明确的类. Apple中我们经常用两种类,一个是框架类,各种用,不断的用,啥都能用:另一个就是协议类,就是协议. 协议的目的,终究是降低一个对象的复杂度和藕合度.框架

iOS设计模式-委托模式

委托是指给一个对象提供机会对另一对象中的变化做出反应或者相应另一个对象的行为.其基本思想是协同解决问题. Delegate的使用场合 对象A内部发生了一些事情,想通知对象B 对象B想监听对象A内部发生了什么事情 对象A想在自己的方法内部调用对象B的某个方法,并且对象A不能对对象B有耦合依赖 对象A想传递数据给对象B 以上情况,结果都一样:对象B是对象A的代理(delegate) 在程序中使用 1.委托(A)需要做的工作有: 1.定义代理协议,协议名称的命名规范:控件类名 + Delegate 2

[js高手之路]设计模式系列课程-委托模式实战微博发布功能

在实际开发中,经常需要为Dom元素绑定事件,如果页面上有4个li元素,点击对应的li,弹出对应的li内容,怎么做呢?是不是很简单? 大多数人的做法都是:获取元素,绑定事件 1 <ul> 2 <li>跟着ghostwu学习javascript设计模式的应用1</li> 3 <li>跟着ghostwu学习javascript设计模式的应用2</li> 4 <li>跟着ghostwu学习javascript设计模式的应用3</li&g

android深入之设计模式(一)委托模式

(一)委托模式简介 委托模式是基本的设计模式之一.委托,即是让另一个对象帮你做事情. 许多其他的模式,如状态模式.策略模式.访问者模式本质上是在更特殊的场合采用了委托模式. 委托模式使得我们可以用聚合来替代继承,java-组合优于继承. 最简单的java委托模式 class RealPrinter { void print() { System.out.println("real printer"); } } class Printer { RealPrinter realPrinte

iOS.常用设计模式.02.委托模式

WTDelegate #import <Foundation/Foundation.h> @protocol WTDelegate <NSObject> @required - (void)sleep; - (void)eat; - (void)work; @end WTPhilosopher.h #import <Foundation/Foundation.h> #import "WTDelegate.h" @interface WTPhiloso

PHP设计模式系列 - 委托模式

委托模式 通过分配或委托其他对象,委托设计模式能够去除核心对象中的判决和复杂的功能性. 应用场景 设计了一个cd类,类中有mp3播放模式,和mp4播放模式 改进前,使用cd类的播放模式,需要在实例化的类中去判断选择什么方式的播放模式 改进后,播放模式当做一个参数传入playList函数中,就自动能找到对应需要播放的方法. 代码:cd类,未改进之前,选择播放模式是一种痛苦的事情 <?php //委托模式-去除核心对象中的判决和复杂的功能性 //使用委托模式之前,调用cd类,选择cd播放模式是复杂的

php设计模式之委托模式

使用委托模式目的是消除潜在的.难以控制的if/else语句. <?php /** * 原来的程序写法 * @var playlist */ $playlist = new playlist(); $playlist->addSong('/home/aaron/music/aa.mp3', 'Brr'); $playlist->addSong('/home/aaron/music/bb.mp3', 'GoodBye'); if ($extType == 'pls') { $playlist

iOS常用设计模式之委托模式

委托模式在Cocoa Touch框架和Cocoa框架中都有很多的应用.例如在应用启动的时候需要的一个类:UIApplication.在程序的入口函数main函数里面: int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([WBAppDelegate class])); } } UIApplication的方法UIAppl

设计模式之委托模式

一:概念 一个对象接收到了请求,但是自己不处理,交给另外的对象处理,就是委托模式,例如 老板接到了活, 然后把活转手给了工人去做. 二:示例 创建一个类IReceiveTask,代表接收任务的动作 public interface IReceiveTask { void receive(String task); } 创建一个类BossService,老板可以接收任务,并且可以命令工人 public class BossService implements IReceiveTask{ priva