c++ 学习之 可调用对象
前言
c++中,在使用一些基于范围的模板函数时,常常需要我们传入一个可调用对象,以指明我们需要对范围中的每个元素进行怎样的处理。在thread的初始化中也需要传入可调用对象来作为线程的入口函数。可调用对象也可以作为回调函数使用。可调用对象大概有这么几种:普通函数 ,类成员函数,类静态函数,仿函数,函数指针,lambda表达式,std::function。下面给大家进行简单的介绍。
正文
1.普通函数
普通函数大家最熟悉不过了,我们常常以定义函数的方式,来讲某一段程序封装在一起,来实现某一特定的功能。以下这种就是普通函数:
#include<stdio.h>
int Add(int a,Int b)
{
return a+b;
}
int main()
{
printf("%d\n",Add(1,3));
}
我们通过调用Add函数来得到两个整数的相加结果。
2.类成员函数
类中定义的函数有两种,一种是类静态函数,另一种是类成员函数。不像类成员变量,两者都有真实的地址。只不过类成员函数中隐含了this指针,也就是说可以在可以在函数内部隐式或显式的使用this指针来调用类的非静态成员。并且类成员函数必须依附于类对象使用,也就是说,在没有对象实例的前提下,是无法通过函数地址使用该函数的。
#include<stdio.h>
class A
{
public:
int a ;
A(int a)
{
this->a =a ;
}
void fun()
{
printf("%d\n",a);
}
};
int main()
{
A a(10);
a.fun();
//A::fun();错误用法!!!
}
输出结果:10
3.类静态函数
上文说到的类中两种函数,类静态函数就是另一种。它与类成员函数最大的不同,就是它不可以调用类中的非静态成员,也不可以使用this指针,但是可以不用创建类对象,直接通过函数地址调用。
#include<stdio.h>
class A
{
public:
static int a;
static int Add(int b)
{
return A::a + b;
}
};
int A::a =1;
int main()
{
printf("%d\n",A::Add(2));
}
输出结果 : 3
可以看到,我们没有创建类的对象,通过直接使用类中的静态函数Add得到类中的静态成员a和2的和。
4.仿函数
通过在类内定义运算符重载函数,用类实现函数调用。
#include<stdio.h>
class A
{
public:
int operator()(int a,int b)
{
return a+b;
}
};
int main()
{
A a;
printf("%d\n",a(1,2));
}
输出结果 : 3
通过在类内部重载(),用类模拟函数的调用。
5.函数指针
函数指针大家并不陌生,我们使用回调函数可以通过函数指针来实现。下面举个例子吧。
#include<stdio.h>
typedef int(*P)(int ,int);
int Add(int a,int b)
{
return a+b;
}
int fun(int p (int,int),int a,int b)
{
return p(a,b);
}
int main()
{
P pAdd = Add;
int a =1;
int b =2;
printf("%d\n", fun(pAdd, a, b));
}
输出结果 :3
虽然这个例子有点牵强,但是意在理解嘛,就是让大家明白,函数指针的使用。往往很多地方需要这样传入一个函数指针作为参数,这样使用就是避免不了的了。其实也可以用函数名作为参数。
6.lambda表达式
lambda 表达式是一种匿名函数,即没有函数名的函数,通常情况下,lambda函数的语法定义为:
[capture] (parame) mutable ->return-type {statement}
其中:
[captures]为捕获列表,用于捕获外层变量
(parame)为匿名函数参数列表
mutable修饰符可以取消返回值的常量性
return-type为返回值类型
{statement}为函数体
通常情况下:
* 可以用auto来定义
* 若没有参数,(parame)可以省略;
* 在默认的情况下,lambda函数总是返回一个const,而当我们在参数列表后面注明了“mutable”关键字之后,则可以取消其常量性质,当我们使用mutable时,(parame)不可以省略,即便参数为空(mutable可以省略);
* return-type可以省略。
* [captures]捕获项可以有0项或多项:
[] 不捕获任何变量
[&] 捕获外部作用域中所有变量,并作为引用在匿名函数体中使用
[=] 捕获外部作用域中所有变量,并拷贝一份在匿名函数体中使用
[&, x] x按值捕获. 其它变量按引用捕获
[=, &y] y按引用捕获. 其它变量按值捕获
[this] 捕获当前类中的this指针,如果已经使用了&或者=就默认添加此选项
*只有lambda函数没有指定任何捕获时,才可以显式转换成一个具有相同声明形式函数指针
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
auto Swap = [&]() {int temp = a; a = b; b = temp; };
Swap();
printf("%d %d\n", a, b);
}
通过匿名函数以引用的方式捕获a,b,在函数中交换a与b的值。
注意,匿名函数在c++ 11 中是inline(内联)的。
7.std::function
std::function 可以用来描述C++中的可调用实体,它可以兼容所有具有相同参数类型的函数实体。需要引入<functional>头文件,声明方式如下:
std::function<Type(type1,type2)> name
下面举几个其他可调用对象转换为std::function的例子:
#include<stdio.h>
#include<functional>
std::function<void(int, int)> Function;
//普通函数
void fun(int a,int b)
{
printf("普通函数:%d\n", a + b);
}
void fun2(int a, int b)
{
printf("函数指针:%d\n", a + b);
}
class A
{
public:
//类成员函数
void fun1(int a, int b)
{
printf("类成员函数:%d\n", a + b);
}
//静态成员函数
static void fun2(int a, int b)
{
printf("静态成员函数:%d\n", a + b);
}
//仿函数
void operator ()(int a, int b)
{
printf("仿函数(类对象):%d\n", a + b);
}
};
int main()
{
int a =1;
int b =2;
Function = fun;
Function(a,b);
void (*p)(int,int) = fun2;
Function = p;
Function(a,b);
A c;
Function = std::bind(&A::fun1,c,std::placeholders::_1,std::placeholders::_2);
Function(a,b);
Function = &A::fun2;
Function(a,b);
A d;
Function = d;
Function(a,b);
auto lam = [](int a,int b){ printf("lambda表达式:%d\n",a+b);};
Function = lam;
Function(a,b);
}
其中需要注意对于类成员函数,因为类成员函数包含this指针参数,所以需要结合使用std::bind函数绑定this指针以及参数列表。
上面例子中:
Function = std::bind(&A::fun1,c,std::placeholders::_1,std::placeholders::_2);
*第一个参数为类成员函数名的引用
*第二个参数为this指针上下文,即特定的对象实例
*使用std::placeholders::_1表示使用调用过程的第1个参数作为成员函数参数,std::placeholders::_n表示调用时的第n个参数。
Function = std::bind(&A::fun1,c,std::placeholders::_1,10);
Function(5);
输出结果 15
Function = std::bind(&A::fun1,c,5,10);
Function();
输出结果 15
Function = std::bind(&A::fun1,c,std::placeholders::_2,std::placeholders::_1);
Function(5,10);
输出结果 15
这样的参数进去,a = 10, b = 5。