0
点赞
收藏
分享

微信扫一扫

C++可调用对象学习笔记

_铁马冰河_ 2022-04-07 阅读 48

文章目录

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 = &sum;

函数指针通常可以和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;
}
举报

相关推荐

0 条评论