仿函数
1.什么是仿函数
1.定义和作用
仿函数是一种重载了函数调用运算符(operator())的类或结构体,它可以像函数一样被调用。仿函数可以在很多STL算法中使用,例如sort、for_each、transform等,可以自定义排序规则、操作、条件等等。通过仿函数,C++程序员可以更加灵活地实现自己的算法。
与普通函数不同,仿函数可以保存状态,因此在使用仿函数时可以灵活地传递参数并进行计算,非常适用于一些复杂的算法和数据结构的实现。
2.仿函数与函数指针的区别
在C++中,函数指针可以作为参数传递和返回值,但是函数指针只能指向函数,无法指向类成员函数和lambda表达式。而仿函数可以作为一种通用的函数封装,可以指向函数、类成员函数以及lambda表达式,并且可以保存状态。因此,仿函数比函数指针更加灵活和可扩展。
3.仿函数的调用方式
仿函数可以像函数一样被调用
class MyFunctor {
public:
void operator() (int i) {
cout << i << endl;
}
};
MyFunctor myFunctor;
myFunctor(123);
//结果
//123
在STL算法中,仿函数也可以作为函数参数传递
vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(vec.begin(), vec.end(), greater<int>());
for_each(vec.begin(), vec.end(), MyFunctor());
其中,greater()是一个内置的仿函数对象,用于实现降序排列。
2.仿函数的分类
1.一元仿函数和二元仿函数
一元仿函数是指只有一个参数的仿函数
template <typename T>
struct MyFunctor1 {
void operator() (T val) {
cout << val << endl;
}
};
template <typename T>
struct MyFunctor2 {
T operator() (T val) {
return val * val;
}
};
二元仿函数是指有两个参数的仿函数
template <typename T>
struct MyFunctor3 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
在STL算法中,一元仿函数和二元仿函数通常用于排序、查找、遍历等操作。
2.函数对象与谓词
- 函数对象:返回值为任意类型的仿函数,例如std::plus,std::minus
- 谓词:返回值为bool类型的仿函数,例如std::less,std::greater
3.函数适配器
函数适配器是一种特殊的仿函数,它用于将一个仿函数适配到另一个仿函数或函数对象上。STL中常用的函数适配器有:bind1st、bind2nd、not1、not2、logical_and、logical_or等
template <typename T>
struct MyFunctor4 {
bool operator() (T val) {
return val > 0;
}
};
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
count_if(vec.begin(), vec.end(), not1(MyFunctor4<int>()));
//结果
//3
其中,not1是一个函数适配器,它将MyFunctor4适配成一个返回相反值的仿函数对象,从而统计出vec中小于等于0的元素个数。
3.仿函数的实现
1.重载函数调用运算符
仿函数的实现首先要重载函数调用运算符(operator()),并根据需要定义参数和返回值
template <typename T>
struct MyFunctor1 {
void operator() (T val) {
cout << val << endl;
}
};
template <typename T>
struct MyFunctor2 {
T operator() (T val) {
return val * val;
}
};
其中,MyFunctor1是一个一元仿函数,用于输出参数值,而MyFunctor2是一个一元仿函数,用于计算参数的平方。
2.使用模板类
仿函数通常是一个模板类,可以支持不同的参数类型
template <typename T>
struct MyFunctor3 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
其中,MyFunctor3是一个二元仿函数,用于比较两个参数的大小。
3.使用函数适配器
仿函数也可以使用函数适配器来实现。例如,使用bind1st函数适配器将一个二元仿函数适配成一个一元仿函数
template <typename T>
struct MyFunctor4 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
MyFunctor4<int> myFunctor;
bind1st(myFunctor, 10)(5);
//结果
//false
其中,bind1st将myFunctor适配成一个只有一个参数的仿函数,第一个参数被绑定为10,然后用5调用这个仿函数,返回false。
4.实现一个简单的加法仿函数
class AddFunctor{
public:
AddFunctor(int n):m_n(n){}
int operator()(int x) const{
return x + m_n;
}
private:
int m_n;
}
在上面的代码中,我们定义了一个AddFunctor类,它带有一个整型参数n,表示每次调用要加上的值。在调用运算符()时,返回x加上m_n的结果。
使用AddFunctor类,可以有如下示例
int main()
{
AddFunctor add(3);
int result = add(5); //结果为8
return 0;
}
4.仿函数的应用
在STL中,许多算法都需要使用仿函数。例如:
- std::sort():对给定的序列进行排序,需要提供一个比较函数,用于指定排序的规则
- std::for_each():对给定的序列中的每个元素执行指定的操作,需要提供一个函数对象,用于指定操作
- std::find_if():在给定的序列中查找符合指定条件的第一个元素,需要提供一个谓词,用于指定条件
通过使用仿函数,我们可以将算法和数据结构解耦,是的算法更加通用和灵活。
5.仿函数的注意事项
在使用仿函数时需要注意以下几点
- 仿函数的效率问题:由于仿函数在调用时需要进行对象的构造和析构,因此在一些需要频繁调用的场景中,使用仿函数可能会影响算法的效率
- 仿函数的线程安全性:由于仿函数中可能会保存状态,因此在多线程环境下使用时需要注意线程安全性问题
6.仿函数的优化
1.使用constexpr
在C++11中,可以使用constexpr关键字来声明一个函数或变量是常量表达式,可以在编译时计算。如果一个仿函数的operator()是一个常量表达式,可以使用constexpr来进行优化
template <typename T>
struct MyFunctor1 {
constexpr T operator() (T val) const {
return val * val;
}
};
这样,当使用MyFunctor1时,如果参数是一个常量表达式,那么编译器就可以在编译时计算出结果,从而提高程序的执行效率。
2.使用inline
仿函数可以使用inline关键字来声明为内联函数,从而在编译时将函数体直接嵌入到调用处,避免了函数调用的开销
template <typename T>
struct MyFunctor2 {
inline T operator() (T val) const {
return val * val;
}
};
3.使用lambda表达式
C++11引入了lambda表达式,可以方便地定义一个匿名仿函数。与普通的仿函数相比,使用lambda表达式可以减少代码的冗余,从而提高程序的可读性和维护性
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
auto count = count_if(vec.begin(), vec.end(), [](int val) { return val > 0; });
其中,lambda表达式[] (int val) { return val > 0; }定义了一个匿名仿函数,用于统计vec中大于0的元素个数。
标签:return,struct,val,vec,operator,函数 From: https://www.cnblogs.com/Vergissmeinnicht-rj/p/17196619.html