文章目录
1 可调用对象简介
理解并运用可调用对象是学习现代C++必不可少的部分。下面带大家详细理解使用这五种可调用对象。
2 可调用对象使用
2.1 函数
先声明或者定义一个函数即可。这样即可使用这个可调用函数。
#include<iostream>
int sum(int a, int b) {
return a + b;
}
int main() {
int a, b;
std::cin >> a >> b;
std::cout << sum(a, b) << std::endl;
return 0;
}
2.2 函数指针
顾名思义,函数指针可以理解为指向函数的指针。可以将函数名赋值给相同类型的函数指针,通过调用函数指针实现调用函数。函数指针是标准的C/C++的回调函数的使用解决方案,本身提供了很大的灵活性。
int(*p_func)(int, int);
这样,即声明了一个函数指针变量,其中指针名的括号必不可少,不然会认为是返回int的指针,使用函数指针可以直接让指针等于函数名,C++规定给函数名取地址和直接使用函数名都是可以得到函数的地址。即:
int sum(int a, int b) {
return a + b;
}
p_func = sum;
p_func = ∑
函数指针通常可以和typedef关键字一同使用,可以使代码更简洁。如:
int sum(int a, int b) {
return a + b;
}
typedef int (*p)(int, int); // 这样就定义了一个函数指针类型,可以用p创建函数指针。
p p_func = sum;
综合实例:
#include <iostream>
// 声明一个p_func函数指针,函数参数为两个int型,返回值为int型
int (*p_func)(int, int);
int max(int x, int y) { return x >= y ? x : y; }
int min(int x, int y) { return x <= y ? x : y; }
int add(int x, int y) { return x + y; }
int multiply(int x, int y) { return x * y; }
// 一个包含函数指针作为回调的函数
int func(int x, int y, int(*p_func)(int, int)) {
// 调用回调函数
return p_func(x, y);
}
int main() {
int x = 2;
int y = 5;
std::cout << "max: " << func(x, y, max) << std::endl; // max: 5
std::cout << "min: " << func(x, y, min) << std::endl; // min: 2
std::cout << "add: " << func(x, y, add) << std::endl; // add: 7
std::cout << "multiply: " << func(x, y, multiply) << std::endl; // multiply: 10
// 无捕获的lambda可以转换为同类型的函数指针
auto sum = [](int x, int y)->int{ return x + y; };
std::cout << "sum: " << func(x, y, sum) << std::endl; // sum: 7
return 0;
}
2.3 lambda表达式
lambda必须使用尾置返回类型来指定返回类型,不能有默认形参,并且具有以下形式:
[capture list](parameter list) -> return_type{function body }
其中:
[capture list]
为捕获列表,用于捕获外层变量(paramter list)
为匿名函数参数列表->return_type
指定匿名函数返回值类型{ function body }
部分为函数体,包括一系列语句
需要注意:
- 当匿名函数没有参数时,可以省略
(paramater list)
部分 - 当匿名函数体的返回值只有一个类型或者返回值为void时,可以省略
->return_type
部分 - 定义匿名函数时,一般使用
auto
作为匿名函数类型
即:
auto func1 = []{}; // 省略参数列表和返回值类型
auto func2 = [](int x) -> int { return x; };
auto func3 = [](int x, int y) {return x + y; }; // 省略返回值类型
func1();
func2(1);
func3(1, 2);
我们可以将lambda表达式和标准库函数相结合使用,来实现我们想要的功能。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
int main() {
std::vector<std::string> strs;
strs.emplace_back("12345");
strs.emplace_back("17");
strs.emplace_back("131");
sort(strs.begin(), strs.end());
std::cout << "default sort results" << std::endl;
for (auto s : strs) {
std::cout << s << std::endl;
}
sort(strs.begin(), strs.end(), [](std::string s1, std::string s2) {
return s1.size() < s2.size();
});
std::cout << "change sort results" << std::endl;
for (auto s : strs) {
std::cout << s << std::endl;
}
return 0;
}
为了能够在Lambda函数中使用外部作用域中的变量,需要在[]
中指定使用哪些变量。
下面是各种捕获选项:
[]
不捕获任何变量[&]
捕获外部作用域中所有变量,并作为引用在匿名函数体中使用。[=]
捕获外部作用域中所有变量,并拷贝一份在匿名函数体中使用[x, &y]
x按值捕获, y按引用捕获[&, x]
x按值捕获. 其它变量按引用捕获[=, &y]
y按引用捕获. 其它变量按值捕获[this]
捕获当前类中的this指针,如果已经使用了&或者=就默认添加此选项
只有lambda函数没有指定任何捕获时,才可以显式转换成一个具有相同声明形式函数指针
auto lambda_func_sum = [](int x, int y) { return x + y; }; // 定义lambda函数
void (*func_ptr)(int, int) = lambda_func_sum; // 将lambda函数赋值给函数指针
func_ptr(10, 20); // 调用函数指针
而有一些标准库的算法的谓词,其形参参数可能不能满足我们的需求,,比如标准库的algorithm里面有find_if函数,它接受一个一元谓词,可以在规定范围内找到满足条件的第一个迭代器,假设现在我们给定一个vector<string>
,我们要找到在整个vector内不大于长度l的第一个迭代器,如果我们一定要使用find_if这个标准库函数来实现这个功能,这个时候我们就要用到lambda的参数捕获了。如:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
int main() {
std::vector<std::string> strs;
strs.emplace_back("17");
strs.emplace_back("131");
strs.emplace_back("12345");
int min_length = 4; // 长度不小于4.
auto iter = find_if(strs.begin(), strs.end(), [&min_length](std::string s) {
return s.size() >= min_length;
});
std::cout << (iter - strs.begin()) << std::endl;
return 0;
}
2.3 bind绑定的对象
简单的来说,bind可以把一些固定的参数和函数绑定,然后调用函数的时候相当于被绑定的参数就不用填了,相当于减少了传入参数的数量。那么2.2中的例子我们可以这样改:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <functional>
bool findMin(const std::string& a, const int& min_length) {
return a.size() >= min_length;
}
int main() {
std::vector<std::string> strs;
strs.emplace_back("17");
strs.emplace_back("131");
strs.emplace_back("12345");
int min_length = 4; // 长度不小于4.
auto iter = find_if(strs.begin(), strs.end(), std::bind(findMin, std::placeholders::_1, min_length));
std::cout << (iter - strs.begin()) << std::endl;
return 0;
}
2.4 重载了函数调用运算符的类对象(即函数对象)
C++的类厉害的地方之一就是可以重载函数运算,就是return_type operator( )(parameter…){ }的形式,这种重载了函数调用符的类,我们创建一个对象,然后调用()运算符模拟函数调用即可。比如:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <functional>
class FuctionObject{
public:
int operator() (const int a, const int b) {
return a + b;
}
};
int main() {
FuctionObject function_object;
std::cout << function_object(2, 3) << std::endl;
return 0;
}