函数对象
定义
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。
class FuncOdject {
public:
void operator()() {
cout << "hello c++!" << endl;
}
}
// 实例化一个对象
FuncOdject val;
val() // cout << "hello c++!" << endl;
// 这有点像使用函数
/*
void val()
{
cout<<"Hello C++!"<<endl;
}
*/
使用函数对象的原因
虽然调用函数和使用函数对象有相同的效果,但函数对象有独特的优势,主要体现在STL的使用。
-
1 . 函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态。但是函数调用没这种优势,除非它使用全局变量来保存状态。
-
2 . 函数对象有自己特有的类型,而普通函数无类型可言。这种特性对于使用C++标准库来说是至关重要的。这样我们在使用STL中的函数时,可以传递相应的类型作为参数来实例化相应的模板,从而实现我们自己定义的规则。比如自定义容器的排序规则。
实例
1. 使用到内部状态的函数对象
// 定义一个函数对象
class SuccessiveNumGen
{
public:
SuccessiveNumGen(int origin = 0):m_origin(origin){}
int operator() ()
{
return m_origin++;
}
private:
int m_origin;
};
vector<int> dest;
generate_n(back_inserter(dest),10,SuccessiveNumGen(3));
此处使用generate_n来表示函数自动调用n次。
back_inserter是一个函数适配器,它返回一个函数对象,并且每次都在dest最后存放元素。
这样,最终容器中的内容为 3,4,5,6,……,12。通过使用不同的起始数字来初始化不同的函数对象,可以生成不同的数字序列。
2. 自定义容器set对字符串string的排序规则
class StringSort {
public:
bool operator() (const string &str1, const string &str2) const
{
return str1 > str2;
}
};
int main() {
set<string,StringSort> myset;
myset.insert("A");
myset.insert("B");
cout << *myset.begin() << endl;
return 0;
}
3. 谓词函数(predicate)
class NoLess
{
public:
NoLess(int min = 0):m_min(min){}
bool operator() (int value) const
{
return value >= m_min;
}
private:
int m_min;
};
int main() {
vector<int> myvec;
myvec.push_back(1);
myvec.push_back(2);
myvec.push_back(10);
remove_if(myvec.begin(), myvec.end(), NoLess(3));
return 0;
}
find_if,remove_if都需要一个谓词函数来实现。而这种函数最好用函数对象来实现。(如果尝试调用函数来实现,会出现错误。而且并不好用,因为每次都要重新改写函数的代码来改变判断的条件。)
意外情况:
有一点需要指出的是,在调用用到函数对象的标准库算法时,除非显式地指定模板类型为“传引用”,否则默认情况下函数对象是“按值传递”的!因此,如果传递的是一个具有内部状态的函数对象,则被改变状态的是函数内部被复制的临时对象,函数结束后随之消失。真正传进来的函数对象状态并为改变。
测试如下:
class Nth
{
public:
Nth(int n=0):m_nth(n),m_count(1){}
bool operator() (int)
{
return m_count++ == m_nth;
}
int GetCount()const
{
return m_count;
}
private:
int m_nth;
int m_count;
};
Nth nth(3);
vector<int>::iterator nthItr = find_if(dest.begin(),dest.end(),nth);
//dest内容为连续数字:3,4,5,6,……,12
cout<<"3rd:"<<*nthItr<<endl;
cout<<"State:"<<nth.GetCount()<<endl;
// 输出结果为,确实能找到第三个数字(5)。
// 但查看nth的状态时,返回的m_count依然为0。说明nth确实未被修改。