在学习C++的时候对这个函数对象还没什么感觉,但是在这次学习Boost.Thread的时候才发现,函数对象的重要性以及方便性。在传统的C线程中,会有一个void*参数用于给线程函数传递参数,但是Boost.Thread去是直接构造线程对象,除了一个函数名之外没有其它的参数,那么如果使用传统的方式(直接将函数名称传入)就只能执行无参数的函数了,所以这里使用了函数对象来实现参数的传递。
(一)函数对象
在来回顾一下什么是函数对象,就是一个重载'()'运算符的类的对象。这样就可以直接使用‘对象名()’的方式,这跟调用函数一样,所以称谓函数对象。看个例子:
1 #include <iostream> 2 #include <string> 3 4 class Printer{ 5 public: 6 explicit Printer(){}; 7 void operator()(const std::string & str)const{ 8 std::cout<<str<<std::endl; 9 } 10 }; 11 12 int main(int atgc,char * argv[]){ 13 Printer print; 14 print("hello world!"); 15 return 0; 16 }
现在来说说函数对象有哪些好处:
(1)函数对象有自己的状态,即它可以携带自己的成员函数,而且这个函数对象在多次调用的过程中它的那些状态是共享的,而函数则不能做到这点(除非定义函数内部的静态变量或者全局变量)。
(2)函数对象有自己的类型,而普通函数则没有。在使用STL的容器时可以将函数对象的类型传递给容器作为参数来实例化相应的模板,从而来定制自己的算法,如排序算法。
假设我需要一个数字产生器,我给定一个初始的数字,然后每次调用它都会给出下一个数字。
#include <iostream> #include <string> #include <vector> #include <algorithm> class SuccessiveNumGen { public: SuccessiveNumGen(int origin = 0):m_origin(origin){} int operator()(){ return m_origin++; } private: int m_origin; }; int main(int argc,char * argv[]){ std::vector<int> dest; generate_n(back_inserter(dest),10,SuccessiveNumGen(3)); for(int i=0;i<10;i++){ std::cout<<dest[i]<<std::endl; } return 0; }
注:此处用到了STL的算法,generate_n会调用SuccessiveNumGen函数十次,back_inserter会将SuccessiveNumGen函数的返回值插入到dest中。
(二)Boost.Thread中函数对象的使用
#include <boost/thread/thread.hpp> #include <iostream> #include <string> void wait(int sec){ boost::this_thread::sleep(boost::posix_time::seconds(sec)); } boost::mutex mutex; class SayHello{ public: explicit SayHello(const std::string & name,int times) :_name(name),_times(times){} void operator()()const{ for(int i=0;i<_times;i++){ wait(1); boost::lock_guard<boost::mutex> lock(mutex); std::cout<<_name<<" says hello ("<<i+1<<")"<<std::endl; } } private: std::string _name; int _times; }; int main(int argc,char * argv[]){ SayHello lux("Lux",5); SayHello jax("Jax",5); boost::thread thr1(lux); boost::thread thr2(jax); thr1.join(); thr2.join(); return 0; }