C++基础知识1:仿函数function objects
本文目的
简单的介绍一下仿函数是什么,仿函数的优点(同时也是他出现的意义)
前置知识点
- C++基本语法基础
- C++的类与模板
- 运算符重载
- 函数指针
仿函数function object简介
又叫函数对象或者函数符,都是指代的仿函数
参考《C++标准库 自修教程与参考手册》8.1 仿函数的概念p293
一个定义了operator(),也就是括号运算符重载的对象。
FunctionObjectType fo;//实例化对象 ... fo();//调用operator()函数
仿函数function object的意义
1. 仿函数的优点
参考《C++标准库 自修教程与参考手册》8.1 仿函数的概念p294
- 可以拥有多个不同状态的实体,而普通函数不行
- 仿函数类别可以当作template参数传递
- 一般比函数指针更快(为什么?)
下面分别对三句话进行说明
2. 仿函数拥有多个不同状态的实体
2.1 构造函数实例化不同仿函数实体
仿函数是在类里面实现的,根据实际需求通过数据成员的变动可以实例化不同的实体;
举个例子下面是一个仿函数的定义,实现0~n的数的打印。
class IntSequence{
private:
int value;
public:
IntSequence(int initialValue):value(initialValue){};
void operator()(){
for(int i = 0; i <= value; i++){
cout<<i<<' ';
}
cout<<'\n';
};
};
int main(int argc, char *argv[])
{
IntSequence seq1(1);
IntSequence seq5(5);
seq1();
seq5();
}
运行输出为如下,不难看出这里通过实例化不同的对象,可以实现仿函数seq1和seq5,功能有区别。也就是出现了具有不同状态的仿函数实体
PS E:\allcode\C++\draft\build\bin> ."E:/allcode/C++/draft/build/bin/draft.exe"
01
012345
这是仿函数能实现的功能之一,但是一般的函数传参也可以实现和上述相同的功能
2.2 运行时数据成员改动,出现不同仿函数实体
不同的仿函数实体存储有自己的数据成员,这一点是普通函数所无法实现的;
运行时候数据成员的改动使得仿函数实体可以实时变化;
比如下面这个例子中,每次运行仿函数实体后输出运行该仿函数实体的次数。
class IntSequence{
private:
int value;
public:
IntSequence():value(1){};
void operator()(){
cout<<value++<<' ';
}
};
};
int main(int argc, char *argv[])
{
IntSequence seq1();
IntSequence seq2();
seq1();
seq2();
seq1();
seq1();
}
输出结果如下:
PS E:\allcode\C++\draft\build\bin> ."E:/allcode/C++/draft/build/bin/draft.exe"
1 1 2 3
调用不同的实体,可以分别输出其运算次数,每个实体又具有自己的成员可以知道自己运行了多少次,这是函数无法实现的。
ps:函数可以利用static变量value,实现自己被调用了多少次,但是无法创建多个实体。
3. 仿函数类别作为模板参数传递
这里以优先队列举个例子,优先队列priority_queue这个容器在实例化的时候需要对模板参数进行说明,如果不记得可以先看优先队列Priority Queues。
根据手册,优先队列模板的第三个参数要求填入一个仿函数类型。
using namespace std;
class myless{
public:
bool operator() (const int& lhs, const int& rhs) const{
return lhs < rhs;
}
};
priority_queue<int,vector<int>,myless> q;
3.1 为什么myless必须是一个类型而不可以是函数?
答:<>里面是显式的表示模板参数,而不是形参一类的东西,没有办法把函数地址(认为是一个实体而不是一个类型)填进去的。
3.2 什么时候使用函数,什么时候使用仿函数类型,什么时候用仿函数实例化的实体?
- 作为模板参数(在单书名号里面)的时候基本上只能填仿函数类型。
- 作为函数形参(在圆括号里面)的时候,如果函数形参是明确的,那就填对应的形参就行。
- 作为模板函数形参的时候,若未显式表明形参类型(没有书名号去显式表示),理论上只要能实现功能,填入函数地址还是仿函数实体都可以,比如下面这个sort函数。
有点抽象,
给出部分具体的例子
- 只能填仿函数类别:优先队列Priority Queues
- 填仿函数实体或者函数地址:排序函数sort
4. 仿函数为什么比函数指针快
仿函数不需要通过函数地址进行函数调用,而是调用了自身的成员函数operator()();
5. 其他
组合型仿函数,预定义仿函数,仿函数二元判断式,如何在函数调用是by reference的传入仿函数实体等。还没学,下次一定。
参考文献
《C++标准库 自修教程与参考手册》8.1 仿函数的概念p293
《C++标准库 自修教程与参考手册》8.1 仿函数的概念p294