C++中str1::function和bind

在C++的TR1中(TechnologyReport)中包括一个function模板类和bind模板函数,使用它们能够实现类似函数指针的功能,但却却比函数指针更加灵活,特别是函数指向类的非静态成员函数时。能够參考Scott Meyers. <<Effective C++ (3rdEdition)>>. Item 35.以下详细说明其用法。

一、指向全局函数或静态成员函数时

由于在本质上讲全局函数和静态成员函数没有差别,用法上除了静态成员函数在引用时要在前面加域作用符className::外。没有其他不论什么差别。其实全局函数也有可能放入命名空间,或者使用全局域作用符,比如 nameSpace::function()或::function,这样不仅本质上同样。形势上也与静态成员函数一致了,所以它们是没有差别的,放到一起讨论。

这样的情况比較简单,仅仅须要定义一个类型

#include

#include

#include

#include

typedef  std::tr1::function  HandlerEvent;

然后再定义一个成员变量

class Sharp{

public:

HandlerEvent handlerEvent;

};

然后在其他函数内就能够通过设置handlerEvent的值来动态装载事件响应函数了,如:

class Rectangle{

private:

std::string name;

Sharp sharp;

public:

void initial(void);

const Sharp getSharp() const;

static void onEvent(int param){ //---------------(1)

std::cout<< "invode onEvent method,get parameter: " << param<< std::endl;

}

};

//类的实现方法

voidRectangle::initial(){

sharp.handlerEvent =HandlerEvent(&Rectangle::onEvent); //---------------(2)

std::cout << "invode initial function!"<< std::endl;

}

const SharpRectangle::getSharp() const{

return sharp;

}

//以下为測试函数:

int main(int argc,char*argv[]){

std::cout <<"hi: " << std::setw(50)<< "hello world!" << std::endl;

Rectangle rectangle;

rectangle.initial();  //---------------(3)

rectangle.getSharp().handlerEvent(23);   //---------------(4)

}

//输出结果例如以下:

hi:                                 helloworld!

invode initialfunction!

invode onEvent method,getparameter: 23    //---------------(5)

注意,这里使用了静态成员函数,假设把Rectangle前面的static去掉这段代码不能工作,编译都不能通过,由于静态成员函数与非静态成员函数的參数表不一样,原型同样的非静态函数比静态成员函数多一个參数,即第一个參数this指针。指向所属的对象,不论什么非静态成员函数的第一个參数都是this指针,所以假设把Rectangle前面的static去掉,其函数原型等效于以下的一个全局函数:

void onEvent(Rectangle*this, int);

所以。这与HandlerEvent所声明的函数类型不匹配,编译将不能通过。并且。既然静态成员函数没有this指针。所以上面(3)处的调用使sharp对象中的handlerEvent使向了Rectangle的静态方法onEvent(),这样当通过(4)处这样调用时就会自己主动运行(1)处的静态函数onEvent()。

二、std::tr1::bind()模板函数的使用

通过上面的std::tr1::function能够对静态成员函数进行绑定。但假设要对非静态成员函数的绑定,需用到以下将要介绍的bind()模板函数.

首先说bind的使用方法,其声明例如以下所看到的:

bind(Function fn, T1 t1, T2 t2, …, TNtN);

当中fn为将被调用的函数,t1…tN为函数的參数。假设不指明參数,则能够使用占位符表示形參,点位符格式为

std::tr1::placehoders::_1,  std::tr1::placehoders::_2, …,  std::tr1::placehoders::_N

将上例中Rectangle::onEvent(int param)前的static去掉改为非静态成员函数,则进行动态绑定使得程序正常执行,将Rectangle::initial(void)的定义改动为:

voidRectangle::initial(){

sharp.handlerEvent =std::tr1::bind(&Rectangle::onEvent,this,std::tr1::placeholders::_1);

std::cout << "invode initial function!"<< std::endl;

}

这样。便动态装载函数成功。

其他測试数据都不用进行改动。測试结果于上一样。

三、指向虚成员函数的使用

对于虚成员函数的情况与上面第2节所说同样,仍然能够实现虑函数的效果。

假设定义类Square继承自Rectangle,将Rectangle::OnEvent重载,定义一个新的Square::OnEvent,Rectangle::initialize中的函数不变,仍然使用Rectangle::OnEvent进进绑定,则调用成员object.onEvent()时,详细运行Rectangle::OnEvent还是Square::OnEvent,看object所属对象的静态类型是Rectangle还是Square而定.

以下为简单演示样例:

我们首先改动一个上面Rectangle的initial()方法,改为虚函数。

如:

virtual voidonEvent(int param){

std::cout<< "invode Rectangle‘s onEventmethod,get parameter: " << param <<std::endl;

}

然后我们再写一个Square类来继承Rectangle类。并重写onEvent方法。如:

class Square : publicRectangle{

public:

void onEvent(intparam){

std::cout<< "invode Square‘s onEventmethod,get parameter: " << param <<std::endl;

}

};

測试代码:

int main(int argc,char *argv[]){

Rectangle rectangle;

rectangle.initial();

rectangle.getSharp().handlerEvent(23);

Square square;

square.initial();

square.getSharp().handlerEvent(33);

}

执行后的结果例如以下:

hi:                                 hello world!

invode initialfunction!

invode Rectangle‘s onEventmethod,get parameter: 23

invode initialfunction!

invode Square‘s onEventmethod,get parameter: 33

这样我们就能够看到sharp会针对详细对象来调用对应的onEvent()方法。 上面的程序演示样例读者可自行研习。

总结:

要注意的地方:

使用的时候一定要注意指向的是没有this指针的函数(全局函数或静态成员函数),还是有this指针的函数。后面一种必需要用bind()函数。并且要多一个參数。
注意bind的參数顺序: bind(&要调用的函数。&对象, 要调用函数的參数1。要调用函数的參数2...,_1(bind函数的參数1),_2(bind函数的參数2)...)
占位符号的參数是由 function 调用的时候传入的。

參数能够由function传入,也可由bind实现绑定传入。这两个是能够调整的。

 

摘录,还有一篇文章 :以boost::function和boost:bind代替虚函数

中心思想是“继承就像一条贼船,上去就下不来了”,而借助boost::function和boost::bind,大多数情况下,你都不用上贼船。

boost::function和boost::bind已经纳入了std::tr1,这也许是C++0x最值得期待的功能,它将彻底改变C++库的设计方式。以及应用程序的编写方式。

Scott Meyers的Effective C++ 3rd ed.第35条款提到了以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把戏。

基于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();

例2:网络库

以boost::function作为桥梁。NetServer class对其使用者没有不论什么类型上的限制,仅仅对成员函数的參数和返回类型有限制。

使用者EchoService也全然不知道NetServer的存在。仅仅要在main()里把两者装配到一起,程序就跑起来了。

// library

class Connection;

class NetServer : boost::noncopyable

{

public:

typedef boost::function<void (Connection*)> ConnectionCallback;

typedef boost::function<void (Connection*, const void*, int len)> MessageCallback;

NetServer(uint16_t port);

~NetServer();

void registerConnectionCallback(const ConnectionCallback&);

void registerMessageCallback(const MessageCallback&);

void sendMessage(Connection*, const void* buf, int len);

private:

// ...

};

// user

class EchoService

{

public:

typedef boost::function<void(Connection*, const void*, int)> SendMessageCallback; // 符合NetServer::sendMessage的原型

EchoService(const SendMessageCallback& sendMsgCb)

: sendMessageCb_(sendMsgCb)

{ }

void onMessage(Connection* conn, const void* buf, int size) // 符合NetServer::NetServer::MessageCallback的原型

{

printf("Received Msg from Connection %d: %.*s/n", conn->id(), size, (const char*)buf);

sendMessageCb_(conn, buf, size); // echo back

}

void onConnection(Connection* conn) // 符合NetServer::NetServer::ConnectionCallback的原型

{

printf("Connection from %s:%d is %s/n", conn->ipAddr(), conn->port(), conn->connected() ?

"UP" : "DOWN");

}

private:

SendMessageCallback sendMessageCb_;

};

// 扮演上帝的角色,把各部件拼起来

int main()

{

NetServer server(7);

EchoService echo(bind(&NetServer::sendMessage, &server, _1, _2, _3));

server.registerMessageCallback(bind(&EchoService::onMessage, &echo, _1, _2, _3));

server.registerConnectionCallback(bind(&EchoService::onConnection, &echo, _1));

server.run();

}

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

一直以来。我对面向对象有一种厌恶感。叠床架屋,绕来绕去的,一拳拳打在棉花上,不解决实际问题。

面向对象三要素是封装、继承和多态。我觉得封装是根本的,继承和多态则是可有可无。

用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而流行起来。

依赖注入与单元測试

前面的EchoService可算是依赖注入的样例,EchoService须要一个什么东西来发送消息。它对这个“东西”的要求仅仅是函数原型满足SendMessageCallback。而并不关系数据究竟发到网络上还是发到控制台。

在正常使用的时候,数据应该发给网络,而在做单元測试的时候。数据应该发给某个DataSink。

安照面向对象的思路。先写一个AbstractDataSink interface。包括sendMessage()这个虚函数,然后派生出两个classes:NetDataSink和MockDataSink,前面那个干活用。后面那个单元測试用。EchoService的构造函数应该以AbstractDataSink*为參数,这样就实现了所谓的接口与实现分离。

我觉得这么做纯粹是脱了裤子放屁,直接传入一个SendMessageCallback对象就能解决这个问题。在单元測试的时候,能够boost::bind()到MockServer上,或某个全局函数上。全然不用继承和虚函数。也不会影响现有的设计。

什么时候使用继承?

假设是指OO中的public继承。即为了接口与实现分离,那么我仅仅会在派生类的数目和功能全然确定的情况下使用。换句话说,不为将来的扩展考虑,这时候面向对象也许是一种不错的描写叙述方法。

一旦要考虑扩展,什么办法都没用。还不如把程序写简单点。将来好大改或重写。

假设是功能继承。那么我会考虑继承boost::noncopyable或boost::enable_shared_from_this。下一篇blog会讲到enable_shared_from_this在实现多线程安全的Signal/Slot时的妙用。

比如,IO-Multiplex在不同的操作系统下有不同的推荐实现。最通用的select()。POSIX的poll()。Linux的epoll(),FreeBSD的kqueue等等,数目固定,功能也全然确定。不用考虑扩展。那么设计一个NetLoop base class加若干详细classes就是不错的解决的方法。

基于接口的设计

这个问题来自那个经典的讨论:不会飞的企鹅(Penguin)到底应不应该继承自鸟(Bird)。假设Bird定义了virtual function fly()的话。讨论的结果是,把详细的行为提出来。作为interface。比方Flyable(能飞的)。Runnable(能跑的),然后让企鹅实现Runnable,麻雀实现Flyable和Runnable。(事实上麻雀仅仅能双脚跳。不能跑,这里不作深究。)

进一步的讨论表明,interface的粒度应足够小,也许包括一个method就够了。那么interface实际上退化成了给类型打的标签(tag)。在这样的情况下。全然能够使用boost::function来取代,比方:

// 企鹅能游泳,也能跑

class Penguin

{

public:

void run();

void swim();

};

// 麻雀能飞,也能跑

class Sparrow

{

public:

void fly();

void run();

};

// 以 closure 作为接口

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

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

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

// 一个既用到run。也用到fly的客户class

class Foo

{

public:

Foo(FlyCallback flyCb, RunCallback runCb) : flyCb_(flyCb), runCb_(runCb)

{ }

private:

FlyCallback flyCb_;

RunCallback runCb_;

};

// 一个既用到run,也用到swim的客户class

class Bar

{

public:

Bar(SwimCallback swimCb, RunCallback runCb) : swimCb_(swimCb), runCb_(runCb)

{ }

private:

SwimCallback swimCb_;

RunCallback runCb_;

};

int main()

{

Sparrow s;

Penguin p;

// 装配起来,Foo要麻雀,Bar要企鹅。

Foo foo(bind(&Sparrow::fly, &s), bind(&Sparrow::run, &s));

Bar bar(bind(&Penguin::swim, &p), bind(&Penguin::run, &p));

}

实现Signal/Slot

boost::function + boost::bind 描写叙述了一对一的回调,在项目中。我们借助boost::shared_ptr + boost::weak_ptr简洁地实现了多播(multi-cast),即一对多的回调,而且考虑了对象的生命期管理与多线程安全;而且,自然地,对使用者的类型不作不论什么限制,篇幅略长,留作下一篇blog吧。(boost::signals也实现了Signal/Slot,但可惜不是线程安全的。)

(总结:事实上在bind函数返回值是一个指针,能够是一个函数指针,在上面Foo和Bar的測试中能够看到,bind函数中第一个參数有几个參数,bind返回的函数指针就有几个參数。也就是说f2 = boost::bind(&Foo::methodInt, &foo, _1); methodInt是一个參数,那么f2就是带有一个參数的函数指针,最后的占位符号是为了让函数函数在调用时使用的,假设不使用占位符,而是直接传递数据。那么在使用f2时传递的參数就不再有效,事实上将f2也能够改为f1,没有严格要求,毕竟这都是一个指针。)

时间: 2024-10-19 12:44:56

C++中str1::function和bind的相关文章

理解 JavaScript 中的 Function.prototype.bind

函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其实就是 Function.prototype.bind(),只是你有可能仍然没有意识到这点. 第一次遇到这个问题的时候,你可能倾向于将this设置到一个变量上,这样你可以在改变了上下文之后继续引用到它.很多人选择使用 self, _this 或者 context 作为变量名称(也有人使用 that)

深入理解javascript中的Function.prototye.bind

函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其实就是 Function.prototype.bind(),只是你有可能仍然没有意识到这点. 第一次遇到这个问题的时候,你可能倾向于将this设置到一个变量上,这样你可以在改变了上下文之后继续引用到它.很多人选择使用 self, _this 或者 context 作为变量名称(也有人使用 that)

浅析 JavaScript 中的 Function.prototype.bind() 方法

Function.prototype.bind()方法 bind() 方法的主要作用就是将函数绑定至某个对象,bind() 方法会创建一个函数,函数体内this对象的值会被绑定到传入bind() 函数的值. 例如,在 f() 函数上调用 bind() 方法并传入参数 obj ,即 f.bind(obj) ,这将返回一个新函数, 新函数会把原始的函数 f() 当做 obj 的方法来调用,就像 obj.f() 似的,当然这时 f() 函数中的 this 对象指向的是 obj . 简单使用情形一 va

2.cocos2dx 3.2中语法的不同之处,lambada表达式的使用和function和bind函数的使用

1        打开建好的T32  Cocos2dx-3.2的一个项目 2        设置Cocos显示窗口的位置是在AppDelegate.cpp中: 3  设置自适应窗口大小的代码是在上面的代码后面紧接着就添加: glview->setDesignResolutionSize(480,320, ResolutionPolicy::EXACT_FIT); 3        cocos2d-x-3.2项目案例(3.2版本之后都去掉了CC前缀) 4        项目目录结构如下: 编写公共

C++11中function和bind的用法示例

环境Visual Studio 2012,具体代码如下 #include <iostream> #include <functional> #include <string> void PrintNumber(int num) { std::cout << num << std::endl; } struct Printer { void Print(std::string print_str) { std::cout << prin

C++ lamda、function、bind使用

参考资料: http://blog.csdn.net/augusdi/article/details/11771699 lambda 表达式的简单语法如下:[capture] (parameters) -> return value { body } 其中[capture]可以选择如下的不同形式: 使用示例: function std::function对象是对C++中现有的可调用实体的一种类型安全的包裹,std::function 使用 bind std::bind是这样一种机制,它可以预先把

std::function,std::bind复习

#include <iostream> #include <functional>//std::bind返回函数对象 void fun1(int a, int b) { std::cout << a << b << std::endl; } using namespace std::placeholders; class A { public: void fun2(int a, int b) { std::cout << a <

《javascript设计模式与开放实践》学习(一)Function.prototype.bind

使用Function.prototype.bind来包装func函数 1.简化版的bind Function.prototype.bind=function (context) { var self=this; //保存原函数 return function () { return self.apply(context,arguments); } }; var obj={name:'seven'}; var func=function(){ alert(this.name); }.bind(ob

【转载】C++ function、bind和lambda表达式

本篇随笔为转载,原贴地址:C++ function.bind和lambda表达式. 本文是C++0x系列的第四篇,主要是内容是C++0x中新增的lambda表达式, function对象和bind机制.之所以把这三块放在一起讲,是因为这三块之间有着非常密切的关系,通过对比学习,加深对这部分内容的理解.在开始之间,首先要讲一个概念,closure(闭包),这个概念是理解lambda的基础.下面我们来看看wikipedia上对于计算机领域的closure的定义: A closure (also le