在學習C++的時候對這個函數對象還沒什麼感覺,但是在這次學習Boost.Thread的時候才發現,函數對象的重要性以及方便性。在傳統的C線程中,會有一個void*參數用於給線程函數傳遞參數,但是Boost.Thread去是直接構造線程對象,除了一個函數名之外沒有其它的參數,那麼如果使用傳統的方式(直接將函數名稱傳入)就只能執行無參數的函數了,所以這裡使用了函數對象來實現參數的傳遞。
(一)函數對象
在來回顧一下什麼是函數對象,就是一個重載'()'運算符的類的對象。這樣就可以直接使用‘對象名()’的方式,這跟調用函數一樣,所以稱謂函數對象。看個例子:
#include <iostream>
#include <string>
class Printer{
public:
explicit Printer(){};
void operator()(const std::string & str)const{
std::cout<<str<<std::endl;
}
};
int main(int atgc,char * argv[]){
Printer print;
print("hello world!");
return 0;
}
現在來說說函數對象有哪些好處:
(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;
}