C++委托实现(函数指针,function+bind,委托模式)

来源:互联网 发布:淘宝面单打印软件 编辑:程序博客网 时间:2024/06/08 03:58

  这一段在公司的某个框架代码中看到了函数指针的使用。风格比较偏纯C,其实C++有更加合适的解决方案,在这里总结一下。
  首先从函数指针说起。
  
  一、函数指针
  从定义上讲,函数指针指向的是函数而非对象,函数指针指向某种特定类型。其类型由函数的返回类型和形参类型决定。
  声明方式如下:

bool (*pf)(const string &,const string &);

  注意pf两侧的括号不能少,不然就变成了单纯的返回值为指针类型的函数声明。
  使用方式如下:
  

bool lengthCompare(const string &a,const string &b){  return a.size()<b.size();}  //same as pf = &lengthCompare  pf = lengthCompare;  bool b1= pf("hello","fun");  cout<<b1<<endl;  //这两个是等价的调用  bool b2 = (*pf)("hello","goodBye");  cout<<b2<<endl;

当然,函数指针也可以作为形参使用,用法如下:

void big(const string &a,const string &b,          bool (*pf)(const string &,const string &)){//same as bool pf(const string &,const string &)  cout<<pf(a,b)<<endl;            }

如果嫌弃参数的表达式过于冗长,可以使用typedef定义类型。

typedef bool func(const string &,const string &);typedef bool (*funcP) (const string &,const string &);

因为在作为参数传递的时候,函数类型会被自动转换为指针,因此有以下两种用法。

void small(const string &a,const string &b,funcP fp){  bool res=fp(a,b);  cout<<!res<<endl;}void smallTwo(const string &a,const string &b,func fp){  bool res=fp(a,b);  cout<<!res<<endl;}

关于C++11的特性这里先不提及,有兴趣的可以查阅decltype与auto。完整的测试代码如下:

#include <iostream>#include <string>using namespace std;bool lengthCompare(const string &a,const string &b){  return a.size()<b.size();}bool (*pf)(const string &,const string &);typedef bool func(const string &,const string &);typedef bool (*funcP) (const string &,const string &);void big(const string &a,const string &b,          bool (*pf)(const string &,const string &)){//same as bool pf(const string &,const string &)  cout<<pf(a,b)<<endl;            }void small(const string &a,const string &b,funcP fp){  bool res=fp(a,b);  cout<<!res<<endl;}void smallTwo(const string &a,const string &b,func fp){  bool res=fp(a,b);  cout<<!res<<endl;}int main(){  //same as pf = &lengthCompare  pf = lengthCompare;  bool b1= pf("hello","fun");  cout<<b1<<endl;  bool b2 = (*pf)("hello","goodBye");  cout<<b2<<endl;  big("hello","goodBye",lengthCompare);  small("hello","goodBye",lengthCompare);  smallTwo("hello","goodBye",lengthCompare);  return 0;}

二、bind+function
  bind和function是boost库中非常重要的部分,boost::function就像C#里的delegate,可以指向任何函数,包括成员函数。当用bind把某个成员函数绑到某个对象上时,我们得到了一个closure(闭包)。(陈硕语)
  这里先介绍一下使用:
  bind可以绑定函数函数对象,以及指向对象成员函数的指针
  先看一下最基础的绑定函数,注意这里和STL中的bind1st等不同之处就在于可以使用多个参数。函数的参数不仅可以储存拷贝,也可以使用引用。

    int g(int a,int b,int c){        return a+b+c;    }       int x=1,y=2,z=3;    //bind with function    cout<<boost::bind(g,_1,_2,_3)(1,2,3)<<endl;    cout<<boost::bind(g,_3,_3,_3)(1,2,3)<<endl;

  下面是函数对象的绑定:
  默认的是绑定函数对象的拷贝,也可以使用ref来储存一个函数对象的引用,具体用法如下:
  

struct F{    int operator()(int a, int b) { return a - b; }    bool operator()(long a, long b) { return a == b; }};struct F2{    int s;    typedef void result_type;    void operator()(int x) { s += x; }};    //bind with function objects    F f;    x=100;    cout<<boost::bind<int>(f,_1,_1)(x)<<endl;    //bind with function objects and store a ref    F2 f2 = { 0 };    int a[] = { 1, 2, 3 };    std::for_each(a, a+3, bind(ref(f2), _1));    cout<<f2.s<<endl;

  指向成员函数的指针不是函数对象,不支持operator()的重载,bind可以接受它们作为第一个参数,用法如下:
  

struct X{    bool f(int a){        cout<<"using bing pointers to members"<<endl;    }};    //using bind with pointers to members    X x1;    shared_ptr<X> p(new X);    int i = 5;    boost::bind(&X::f, &x1, _1)(i);         // (&x)->f(i)    boost::bind(&X::f, x1, _1)(i);          // (internal copy of x).f(i)    boost::bind(&X::f, p, _1)(i);           // (internal copy of p)->f(i)

  前面介绍过,function使用返回类型和参数来完成类型函数模板的功能。什么如下:
  

boost::function<float (int x, int y)> f;

  如果没有bind,function并没有什么特别用处,二者结合,就可以实现一种委托的功能。“同一个类的不同对象可以delegate给不同的实现,从而实现不同的行为”(myan语)。两者结合使用如下(陈硕的小例子):
  

#include <iostream>#include <boost/bind.hpp>#include <boost/function.hpp>#include <algorithm>#include <memory>using namespace std;void sum(int a,int b){  cout<<"sum is :"<<a+b<<endl;}class Foo{ public:  void methodA(){    cout<<"methodA"<<endl;  }  void methodInt(int a){    cout<<"methodInt:"<<a<<endl;  }};class Bar{ public:  void methodB(){    cout<<"methodB"<<endl;  }};int main(){  boost::function<void(int,int)> f;  f=&sum;  f(3,4);  boost::function<void()> f1; // 无参数,无返回值  Foo foo;  f1 = boost::bind(&Foo::methodA, &foo);  f1(); // 调用 foo.methodA();  Bar bar;  f1 = boost::bind(&Bar::methodB, &bar);  f1(); // 调用 bar.methodB();  f1 = boost::bind(&Foo::methodInt, &foo, 42);  f1(); // 调用 foo.methodInt(42);  boost::function<void(int)> f2; // int 参数,无返回值  f2 = boost::bind(&Foo::methodInt, &foo, _1);  f2(53); // 调用 foo.methodInt(53);}

  乍看起来,似乎这些用法和函数指针的用法没有什么差别,有点无端炫技的感觉,其实不然。虽然本质一样,但是对象可以携带状态,有自己的数据存储,这就大大提高了函数指针的可用性。

三、委托模式
  委托模式又称作代理模式,在GOF的《设计模式:可复用面向对象软件的基础》一书中对代理模式是这样说的:为其他对象提供一种代理以控制对这个对象的访问。
  如果使用过翻墙浏览器的,应该对代理这个词不陌生。代理就是代替你和外界做交互,这里为什么提到这个设计模式,因为从本质上讲,上面函数指针的用法就是通过一个Proxy这样的东西,将你所要处理的东西,或者请求转发给某个函数或者对象。
  UML类图
Proxy:

  1. 保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,Proxy会引用Subject,就相当于在代理类中保存一个Subject指针,该指针会指向RealSubject;
  2. 提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体;
  3. 控制对实体的存取,并可能负责创建和删除它;
  4. 其它功能依赖于代理的类型,例如: 远程代理负责对请求及其参数进行编码,并向不同地址空间中的实体发送已编码的请求;
    虚代理可以缓存实体的附加信息,以便延迟对它的访问;
    保护代理检查调用者是否具有实现一个请求所必须的访问权限。

Subject:

  定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy;

RealSubject:定义Proxy所代理的实体。

最简单的实现就是对上面类图的实现:

#include <iostream>using namespace std;#define SAFE_DELETE(p) if (p) { delete p; p = NULL;}class CSubject{public:    CSubject(){};    virtual ~CSubject(){}    virtual void Request() = 0;};class CRealSubject : public CSubject{public:    CRealSubject(){}    ~CRealSubject(){}    void Request()    {        cout<<"CRealSubject Request"<<endl;    }};class CProxy : public CSubject{public:    CProxy() : m_pRealSubject(NULL){}    ~CProxy()    {        SAFE_DELETE(m_pRealSubject);    }    void Request()    {        if (NULL == m_pRealSubject)        {            m_pRealSubject = new CRealSubject();        }        cout<<"CProxy Request"<<endl;        m_pRealSubject->Request();    }private:    CRealSubject *m_pRealSubject;};int main(){    CSubject *pSubject = new CProxy();    pSubject->Request();    SAFE_DELETE(pSubject);}

更复杂的例子可以参考智能指针。

0 0
原创粉丝点击