以boost::function和boost:bind取代虚函数

基本用途编程


boost::function就像C#里的delegate,能够指向任何函数,包括成员函数。当用bind把某个成员函数绑到某个对象上时,咱们获得了一个closure(闭包)。例如:设计模式

class Foo

{

 public:

  void methodA();

  void methodInt(int a);

};

class Bar

{

 public:

  void methodB();

};


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);

若是没有boost::bind,那么boost::function就什么都不是,而有了bind(),“同一个类的不一样对象能够delegate给不一样的实现,从而实现不一样的行为”(myan语),简直就无敌了。闭包


对程序库的影响编程语言


程序库的设计不该该给使用者带来没必要要的限制(耦合),而继承是仅次于最强的一种耦合(最强耦合的是友元)。若是一个程序库限制其使用者必须从某个class派生,那么我以为这是一个糟糕的设计。不巧的是,目前有些程序库就是这么作的。函数


例1:线程库工具


常规OO设计: 线程


写一个Thread base class,含有(纯)虚函数 Thread#run(),而后应用程序派生一个继承class,覆写run()。程序里的每一种线程对应一个Thread的派生类。例如Java的Thread能够这么用。设计

 

缺点:若是一个class的三个method须要在三个不一样的线程中执行,就得写helper class(es)并玩一些OO把戏。code


基于closure的设计: 面向对象设计模式


令Thread是一个具体类,其构造函数接受Callable对象。应用程序只需提供一个Callable对象,建立一份Thread实体,调用Thread#start()便可。Java的Thread也能够这么用,传入一个Runnable对象。C#的Thread只支持这一种用法,构造函数的参数是delegate ThreadStart。boost::thread也只支持这种用法。

// 一个基于 closure 的 Thread class 基本结构

class Thread 

{ 

 public: 

  typedef boost::function<void()> ThreadCallback; 

  Thread(ThreadCallback cb) : cb_(cb) 

  { } 

  void start() 

  { 

    /* some magic to call run() in new created thread */ 

  } 

 private: 

  void run() 

  { 

    cb_(); 

  } 

  ThreadCallback cb_; 

  // ... 

}; 


使用:

class Foo

{

 public:

  void runInThread();

};


Foo foo;

Thread thread(boost::bind(&Foo::runInThread, &foo));

thread.start();

对面向对象程序设计的影响


一直以来,我对面向对象有一种厌恶感,叠床架屋,绕来绕去的,一拳拳打在棉花上,不解决实际问题。面向对象三要素是封装、继承和多态。我认为封装是根本的,继承和多态则是无关紧要。用class来表示concept,这是根本的;至于继承和多态,其耦合性太强,每每不划算。

继承和多态不只规定了函数的名称、参数、返回类型,还规定了类的继承关系。在现代的OO编程语言里,借助反射和attribute/annotation,已经大大放宽了限制。举例来讲,JUnit 3.x 是用反射,找出派生类里的名字符合 void test*() 的函数来执行,这里就没继承什么事,只是对函数的名称有部分限制(继承是全面限制,一字不差)。至于JUnit 4.x 和 NUnit 2.x 则更进一步,以annoatation/attribute来标明test case,更没继承什么事了。

个人猜想是,当初提出面向对象的时候,closure尚未一个通用的实现,因此它没能算做基本的抽象工具之一。如今既然closure已经这么方便了,或许咱们应该从新审视面向对象设计,至少不要那么滥用继承。

自从找到了boost::function+boost::bind这对神兵利器,不用再考虑类直接的继承关系,只须要基于对象的设计(object-based),拳拳到肉,程序写起来顿时顺手了不少。


对面向对象设计模式的影响


既然虚函数能用closure代替,那么不少OO设计模式,尤为是行为模式,失去了存在的必要。另外,既然没有继承体系,那么建立型模式彷佛也没啥用了。

最明显的是Strategy,不用累赘的Strategy基类和ConcreteStrategyA、ConcreteStrategyB等派生类,一个boost::function<>成员就解决问题。在《设计模式》这本书提到了23个模式,我认为iterator有用(或许再加个State),其余都在摆谱,拉虚架子,没啥用。或许它们解决了面向对象中的常见问题,不过要是个人程序里连面向对象(指继承和多态)都不用,那彷佛也不用叨扰面向对象设计模式了。

或许closure-based programming将做为一种新的programming paradiam而流行起来。

相关文章
相关标签/搜索