仿函数
定义:仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator这个类就有了类似函数的行为,就是一个仿函数类了。
有时会发现有些功能实现的代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立出来成为一个类的一个成员函数。但是又很想复用这些代码。
写一个公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。
这时就可以用仿函数了,写一个简单类,除了那些维护一个[类的成员函数]外,就只是实现一个operator(),在类实例化时,就将要用的,非参数的元素传入类中。这样就免去了对一些公共变量的全局化的维护了。又可以使那些代码独立出来,以便下次复用。
而且这些仿函数,还可以用关联,聚合,依赖的类之间的关系,与用到他们的类组合在一起,这样有利于资源的管理(这点可能是它相对于函数最显著的优点了)。如果在配合上模板技术和policy编程思想,那就更是威力无穷了
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
class CompareObject{
public:
void operator()(){
cout << "仿函数" << endl;
}
void operator()(int number,int number2){
cout << "仿函数" << endl;
}
};
// 查看c++ for_each源码自定义
class ShowActionObj{
public:
void operator()(int content){
cout << "custom 仿函数" << content << endl;
}
};
// 回调方式
void showAction(int content){
cout << "custom 一元谓词" << content << endl;
}
int main(){
// 谓词 == 仿函数
CompareObject fun1;
fun1();
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
for_each(setVar.begin(),setVar.end(),ShowActionObj());
cout << "---" << endl;
for_each(setVar.begin(),setVar.end(),showAction);
return 0;
}
再写C++ STL 中总结了谓词,相当于仿函数
谓词 <-> 仿函数(空谓词 一元谓词 二元谓词 三元谓词)
C#是通过委托delegate来实现仿函数的。
Java中的仿函数是通过实现包含单个函数的接口实现的
C语言使用[函数指针]和[回调函数]来实现仿函数,例如一个用来排序的函数可以这样使用仿函数
List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12");
Comparator<String> numStringComparator =new Comparator<String>(){
publicint compare(String o1, String o2){
returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2));
}
};
Collections.sort(list, numStringComparator);
#include <stdlib.h>
/* Callback function */
int compare_ints_function(void*A,void*B)
{
return*((int*)(A))<*((int*)(B));
}
/* Declaration of C sorting function */
void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*));
int main(void)
{
int items[]={4,3,1,2};
sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function);
return 0;
}
回调函数,谓词,仿函数 分析
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
// 我如何阅读C++源码,来写我们的仿函数
// 明明白白的仿函数(一元谓词==一元函数对象)
class showActionObj {
public:
void operator()(int content) {
cout << "自定义仿函数" << content << endl;
}
};
// 回调函数 如果叫 仿函数 有点合理
// 简洁方式(回调函数、一元谓词 但是不能称为 仿函数)
void showAction(int content) {
cout << "自定义 一元谓词" << content << endl;
}
using namespace std;
int main() {
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
// for_each(setVar.begin(), setVar.end(), showActionObj());
for_each(setVar.begin(), setVar.end(), showAction);
return 0;
}
C++ 中,STL + 算法包 + 迭代器 是分开的。所以我们需要手动组合。
image.pngfor_each: 源码
for_each.png
- 回调函数 (功能够简单)
- 仿函数(扩展性强) C++内置源码使用仿函数频率高,扩展性强
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
// 回调函数 (功能够简单)
void showAction(int __first) {
cout << "一元谓词" << __first << endl;
}
// 仿函数(扩展性强) C++内置源码使用仿函数频率高,扩展性强
class showActionObj {
public:
int count = 0;
void _count() { cout << "本次输出次数是:" << this->count << endl; }
void operator() (int __first) {
cout << "仿函数" << __first << endl;
count++;
}
};
int main() {
// 理解:类型传递
// set<int, showActionObj> setVar; 这样写的语法是OK的,不能加括号
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
// TODO 第一种方式
for_each(setVar.begin(), setVar.end(), showAction);
// 请你统计打印次数? 答:做不到
// TODO 第二种方式
showActionObj s; // 理解:值传递
s = for_each(setVar.begin(), setVar.end(), s); // 传入进去的s是新的副本,我们外面的s是旧地址
// 请你统计打印次数? 答:OK
s._count();
return 0;
}
看下_Function
image.png
可以解释这句话
s = for_each(setVar.begin(), setVar.end(), s); // 传入进去的s是新的副本,我们外面的s是旧地址
仿函数 能做到的封装,回调函数是做不到的。源码用到了大量仿函数,证明它的扩展性好。
解决 赋值问题说明
类型传递
的 仿函数
怎么看源码得知写法
set<string> setVar;
点击进入源码查看
看到这部分模板定义
template <class _Key, class _Compare = less<_Key>,
class _Allocator = allocator<_Key> >
第一个参数就是我们<> 里面的类型,第二个参数,可以不写,默认参数less
less ,public继承了binary_function
struct _LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool>
{
_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
bool operator()(const _Tp& __x, const _Tp& __y) const
{return __x < __y;}
};
这样做,思想和RxJava一致,做到统一过滤用,有成百上千的操作符。
父类就可以得到我的子类仿函数,根据这个,我们就可以自定义仿函数。仿造源码,写一个二元谓词
#include <iostream>
#include <set>
using namespace std;
// C++源码:typename _Compare = std::less less内置的仿函数,根据内置仿函数去写 自定义
// bool operator()(const _Tp& __x, const _Tp& __y) const 二元谓词
class CompareObjectClass {
public:
bool operator() (const string & __x, const string & __y) const { // const 指针 const 常量指针常量 = 只读
return __x > __y;
}
};
int main() {
set<string, CompareObjectClass> setVar; // 第一版
setVar.insert(setVar.begin(), "AAAAAAA");
setVar.insert(setVar.begin(), "BBBBBBB");
setVar.insert(setVar.begin(), "CCCCCCC");
setVar.insert(setVar.begin(), "DDDDDDD");
setVar.insert(setVar.begin(), "EEEEEEE");
setVar.insert(setVar.begin(), "FFFFFFF");
// 迭代器 循环
for (set<string>::iterator iteratorVar = setVar.begin(); iteratorVar != setVar.end(); iteratorVar++) {
cout << "循环item:" << *iteratorVar << "\t";
// 循环item:AAAAAAA 循环item:BBBBBBB 循环item:CCCCCCC 循环item:DDDDDDD 循环item:EEEEEEE 循环item:FFFFFFF
// 循环item:FFFFFFF 循环item:EEEEEEE 循环item:DDDDDDD 循环item:CCCCCCC 循环item:BBBBBBB 循环item:AAAAAAA
}
return 0;
}
通过Set 的内置反函数less, 内置反函数,定义我们自己的函数
容器存储对象,生命周期
set 存入对象 奔溃(set会自动排序,对象没法排序,所以奔溃) 解决方案:自定义仿函数解决
为了方便避免重复,我们用vector存储对象,说明生命周期
#include <iostream>
#include <set> // set 存入对象 奔溃(set会自动排序,对象没法排序,所以奔溃) 解决方案:自定义仿函数解决
#include <vector> // 存入对象
using namespace std;
class Person {
private:
string name;
public:
Person(string name) : name(name) {}
void setName(string name) {
this->name = name;
}
string getName() {
return this->name;
}
Person(const Person &person) {
this->name = person.name; // 浅拷贝
cout << "Person拷贝构造函数执行了..." << endl;
}
~Person() {
cout << "Person析构函数执行了" << endl;
}
};
int main() {
// Java:把对象存入 添加到 集合
// C++: 调用拷贝构造函数,存进去的是另一个新的对象
vector<Person> vectorVar;
// person 被main函数弹栈 析构一次
Person person("David"); // 2 David
// 里面的insert函数弹栈 析构一次
vectorVar.insert(vectorVar.begin(), person); // 外面的person是旧地址,到insert函数里面的person就是新地址(拷贝构造函数 一次)
person.setName("Kevin"); // 1
// newPerson 被main函数弹栈 析构一次
Person newPerson =
vectorVar.front(); // front里面的person是旧地址, 外面的newPerson就是新地址(拷贝构造函数 一次)
cout << "newPerson:" << newPerson.getName().c_str() << endl;
// 3次析构函数 两次拷贝构造
return 0;
} // main弹栈
代码执行了,3次析构函数 两次拷贝构造
Person person("David")
Person newPerson =vectorVar.front()
执行拷贝构造函数 front里面的是旧的地址,外面的newPerson 就是新的地址
Person person("David")
main函数执行完会析构一次
vectorVar.insert(vectorVar.begin(), person) i
nsert 内部结束也会析构一次
Person newPerson =vectorVar.front()
newPerson 被main函数弹栈 析构一次
预定义函数
C++ 内置函数
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
int main() {
// "David" + "AAAA" // 运算符重载
// C++已经提供了 预定义函数 plus,minus,multiplies,divides,modulus ...
plus<int> add_func;
int r = add_func(1, 1);
cout << r << endl;
plus<string> add_func2;
string r2 = add_func2("AAAA", "BBB");
cout << r2 << endl;
plus<float> add_func3;
float r3 = add_func3(4354.45f, 34.3f);
cout << r3 << endl;
return 0;
}
简单的例子,plus 。 我们拼接字符串可能想到运算符重载,但是c++内置plus解决了这个问题。
有些比如,对象可能就不适用,我们可以手写预定义函数
: public binary_function_t<T, T, T>
可以要可以不要
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
template<typename Arg1, typename Arg2, typename Result>
struct binary_function_t
{
/// 第一个参数类型 是底一个参数的类型
typedef Arg1 first_argument_type;
//econd_argument_type是第二个参数的类型
typedef Arg2 second_argument_type;
/// @c result_type是返回类型
typedef Result result_type;
};
// TODO 对象 + 对象
// 1.运算符重载
// 2.对象+对象 自己去写仿函数
template<typename T>
struct plus_d : public binary_function_t<T, T, T>
{
T operator() (const T & x, const T & y) {
return x + y;
}
};
int main() {
plus_d<int> add_func;
int r = add_func(1, 1);
cout << r << endl;
plus_d<string> add_func2;
string r2 = add_func2("AAAA", "BBB");
cout << r2 << endl;
plus_d<float> add_func3;
float r3 = add_func3(4354.45f, 34.3f);
cout << r3 << endl;
return 0;
}
网友评论