函数指针简介
函数指针指向某种特定类型,函数的类型由其参数及返回类型共同决定,与函数名无关 。
int callWithMax(int a, int b)//函数定义
这个函数的返回值是int,传入的两个参数也是int,所以这个函数的类型就是 int(int, int)类型,我们可以声明一个指向该函数的函数指针,声明如下:
int (*p)(int, int)//此时还未初始化
这样的话这个函数指针就可以指向所有的int(int, int)类型的函数,我们让它指向callWithMax函数。
p = callWithMax;//等同于p = &callWithMax;
这样的话p就可以当作callWithMax函数用了。
源码:
#include<iostream>
int callWithMax(int a, int b){
return a > b ? a : b;
}
int (*p)(int, int);
int main(){
p = &callWithMax;
std::cout << p(1,2);
return 0;
}
输出 :
函数指针在c++11中的声明
1. typedef与decltype组合定义函数类型
按如下声明的话,pf就是一个函数类型,它和callWithMax类型相同。decltype()返回传入参数的数据类型。
typedef decltype(callWithMax) pf;
源码:
#include<iostream>
int callWithMax(int a, int b){
return a > b ? a : b;
}
typedef decltype(callWithMax) pf;
int main(){
pf* max;
max = callWithMax;
std::cout << max(1,2);
return 0;
}
输出:
你也可以直接使用如下直接声明函数指针
typedef decltype(callWithMax)* pf;
这样使用的话直接使用以下声明即可:
pf max;
max = callWithMax;
2.使用auto自动匹配函数类型
auto可以直接判断数据类型,因此这种写法最简单
auto max = callWithMax;
源码:
#include<iostream>
int callWithMax(int a, int b){
return a > b ? a : b;
}
int main(){
auto max = callWithMax;
std::cout << max(1,2);
return 0;
}
输出:
std::function
1.可调用对象
定义:
- 是一个函数指针;
- 是一个具有operator()成员函数的类的对象;
- 可被转换成函数指针的类对象;
- 一个类成员函数指针;
我们可以看以下三种写法:
int Max(int a, int b){return a > b ? a : b;}//普通函数
auto Max_lambda = [](int a, int b){return a > b ? a : b;}//lambda表达式
//仿函数
class Max_class{
public:
int operator()(int a, int b){
return a > b ? a : b;
}
}
这三种写法都是同一个调用对象类型
int(int, int)
而std::function就可以保存这个类型
std::function<int(int, int)> m1 = Max;
std::function<int(int, int)> m2 = Max_lambda;
std::function<int(int, int)> m3 = Max_class();
定义格式:std::function<函数类型>。
std::function 是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
std::function可以取代函数指针的作用,因为它可以延迟函数的执行,特别适合作为回调函数使用。它比普通函数指针更加的灵活和便利。
std::bind
std::bind可以看作为是一个函数适配器,它接收一个可调用对象和它的参数,将其绑定为一个新的参数列表的可调用对象。
- 将可调用对象和其参数绑定成一个仿函数;
- 只绑定部分参数,减少可调用对象传入的参数。
对函数使用std::bind
源码1:
#include<iostream>
/* 引入bind的头文件 */
#include <functional>
/* _1 下标的命名空间 */
using namespace std::placeholders;
double m_div(double a, double b){
return a / b;
}
int main(){
/* 给m_div绑定两个参数,第一个参数是函数名,第二个参数是_1也就是一个记号,以后传入的参数将代替这个位置(m_div的第一个参数) ,第三个参数是m_div的第二个参数*/
auto half = std::bind(m_div,_1,2);
std::cout << half(10);
return 0;
}
输出:
上述“auto half = std::bind(m_div,_1,2);”相当与声明了一个新的函数half,它的实现如下:
double m_div(double a/*, double b = 2*/){
return a / b;
//return a / 2;
}
源码2:
#include<iostream>
#include <functional>
double m_div(double a, double b){
return a / b;
}
using namespace std::placeholders;
int main(){
auto div_ = std::bind(m_div,_1,_2);//标记两个参数
std::cout << div_(10, 5);
return 0;
}
输出:
对成员函数使用std::bind
源码:
#include<iostream>
#include <functional>
class Div{
public:
double m_div(double a, double b){
return a / b;
}
};
using namespace std::placeholders;
int main(){
Div d;
//第一个参数必须是&类名::函数名,第二个参数是类的地址,然后是参数列表下标
auto helf = std::bind(&Div::m_div, &d,_1,_2);
std::cout << helf(10, 5);
return 0;
}
输出:
绑定一个引用参数
默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中。但是,与lambda类似,有时对有些绑定的参数希望以引用的方式传递,或是要绑定参数的类型无法拷贝。(以下代码引用自博客:https://www.jianshu.com/p/f191e88dcc80)
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <sstream>
using namespace std::placeholders;
using namespace std;
ostream & print(ostream &os, const string& s, char c)
{
os << s << c;
return os;
}
int main()
{
vector<string> words{"helo", "world", "this", "is", "C++11"};
ostringstream os;
char c = ' ';
for_each(words.begin(), words.end(),
[&os, c](const string & s){os << s << c;} );
cout << os.str() << endl;
ostringstream os1;
// ostream不能拷贝,若希望传递给bind一个对象,
// 而不拷贝它,就必须使用标准库提供的ref函数
for_each(words.begin(), words.end(),
bind(print, ref(os1), _1, c));
cout << os1.str() << endl;
}