云计算百科
云计算领域专业知识百科平台

【知识讲解】 C语言函数指针的摇身一变--C++11包装器的介绍与其在算法题目中的使用


目录

前言

Part1. Lambda表达式捕获机制详解

Part1.1. 值捕获基础特性

Part1.2. 捕获简写语法

Part1.3. mutable关键字

Part1.4. lambda底层原理

Part2. C++11 类的六大默认成员函数

Part2.1. 六大默认函数

Part2.2. C++11规则

Part2.3. 类型分类

Part2.4. = default关键字

Part3. std::function 函数包装器

Part3.1. 作用

Part3.2. 基础语法

Part3.3. 包装类成员函数

Part3.4. 调用规则

Part3.5. 包装器与逆波兰表达式

Part4. std::bind 参数绑定工具

Part4.1. 核心能力

Part4.2. 占位符规则

Part4.3. bind 结合function处理成员函数

Part5. 结语


前言

  C++11包装器,lambda表达式以及bind的知识是非常重要的。在项目实践,算法题的实现中都有所体现,我们来看看吧。


let's go!!!!!!!!


Part1. Lambda表达式捕获机制详解

Part1.1. 值捕获基础特性

int a = 0;
int b = 1;
auto f = [a, &b](int x) {
    a++;
    b += x;
};
//[a]:值捕获,仅拷贝外部变量,lambda内部修改的只是副本,不会影响原始变量。

//[&b]:引用捕获,直接绑定外部原始变量,内部修改会作用于外部。
//重点:同一个变量不能同时捕获两次(既值捕获又引用捕获)。


Part1.2. 捕获简写语法

<1> [=]:全部值捕获。函数体内出现的外部变量,自动以值拷贝捕获

<2> [&]:全部引用捕获。函数体内出现的外部变量,自动以引用捕获

<3> [=, &a, &b]:默认值捕获,单独指定a、b采用引用捕获;

<4> [&, a, b]:默认引用捕获,单独指定a、b采用值捕获。

注意:lambda不能递归。因为lambda类型无法提前声明,无法在捕获列表内引用自身。


Part1.3. mutable关键字

auto f = [a](int x) mutable {
    a++;
};
//值捕获得到的成员变量,默认是const属性,不允许修改;
//添加mutable后,取消const限制,允许修改捕获的副本。
//再次强调:修改的依旧是拷贝副本,无法改变外部原始变量。


Part1.4. lambda底层原理

Lambda不是语法糖,编译器会自动生成一个匿名仿函数类

<1> 捕获的外部变量,会变成仿函数类的成员变量

<2> 参数列表、函数体对应仿函数的operator()

<3> 返回值类型由编译器自动推导,也可以显式-> Type指定


Part2. C++11 类的六大默认成员函数

Part2.1. 六大默认函数

<1> 默认构造函数

<2> 拷贝构造函数

<3> 拷贝赋值运算符

<4> 移动构造函数

<5> 移动赋值运算符

<6> 析构函数


Part2.2. C++11规则

如果自己没有手动实现,编译器自动生成默认版本。

一旦手动实现其中某一个,会影响其余默认函数的隐式生成。


Part2.3. 类型分类

<1> 内置类型成员:默认构造有时会初始化;

<2> 自定义类成员:调用自身默认构造。


Part2.4. = default关键字

Person(Person&& p) = default;
//强制让编译器生成默认版本的移动构造函数,即便你已经手动编写了其他构造函数。
//补充:= delete 则是显式删除函数,禁止调用。


Part3. std::function 函数包装器

头文件:#include <functional>


Part3.1. 作用

  统一包装任何可调用对象:普通函数、函数指针、lambda、仿函数、类成员函数; 提供统一的类型,能够存放在容器(map/vector)中,实现回调管理。


Part3.2. 基础语法

// 模板格式:function<返回类型(参数类型列表)>
std::function<int(int,int)> f;

// 1.包装普通函数
int add(int a,int b){return a+b;}
f = add;

// 2.包装lambda表达式
f = [](int a,int b){return a+b;};


Part3.3. 包装类成员函数

成员函数隐藏参数this,使用function包装分两种情况:

<1> 静态成员函数

静态成员不存在this,直接正常包装:
struct A{
    static int fun(int a,int b){
        return a-b;
    }
};
std::function<int(int,int)> f = A::fun;
<2> 非静态成员函数

必须额外传入对象(对象/对象指针),对应隐式this参数:
struct A{
    int fun(int a,int b){
        return a-b;
    }
};
A a1;
// 第一种写法:绑定对象实例
std::function<int(int,int)> f = std::bind(&A::fun, a1, _1, _2);

// 第二种:绑定对象指针
std::function<int(int,int)> f = std::bind(&A::fun, &a1, _1, _2);
参数占位符 _1、_2、_3 定义在命名空间 std::placeholders。


Part3.4. 调用规则

int res = f(10,20); 调用时传入参数,占位符_1匹配第一个实参、_2匹配第二个实参。


Part3.5. 包装器与逆波兰表达式


class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> aaa;
map<string,function<int(int,int)>> F={
{"+",[](int a,int b){return a+b;}},
{"-",[](int a,int b){return a-b;}},
{"/",[](int a,int b){return a/b;}},
{"*",[](int a,int b){return a*b;}}
};//将包装器与容器结合
for(int i=0;i<tokens.size();i++)
{
if(F.find(tokens[i])==F.end())
{
aaa.push(stoi(tokens[i]));
}
else
{
int a=aaa.top();
aaa.pop();
int b=aaa.top();
aaa.pop();
aaa.push(F[tokens[i]](b,a));
}
}
return aaa.top();
}
};


Part4. std::bind 参数绑定工具

头文件:#include <functional>


Part4.1. 核心能力

<1> 固定函数某些参数

<2> 重新调整参数顺序

<3> 配合std::function包装成员函数。


Part4.2. 占位符规则

std::placeholders::_1、_2……
//含义:代表调用bind返回的函数时,传入的第N个参数。

示例:
using namespace std::placeholders;
int sub(int a,int b){
    return a – b;
}
// 固定第一个参数为100,剩余参数由调用时传入
auto sub1 = bind(sub, 100, _1);
sub1(5); // 等价 sub(100,5) → 95

// 调换参数顺序
auto sub2 = bind(sub, _2, _1);
sub2(10,5); // 等价 sub(5,10) → -5


Part4.3. bind 结合function处理成员函数

struct A{
    int calc(int x,int y){
        return x+y;
    }
};
A obj;
// bind绑定成员函数 + 对象实例 + 占位符
function<int(int,int)> f = bind(&A::calc, obj, _1, _2);

原理: bind返回一个可调用对象; std::function可以接收该对象进行存储; 非静态成员函数依赖this,bind会捕获对象,后续调用不需要再传递实例。


Part5. 结语

  这篇文章我们认识到了C++11相关知识,接下来,小编还会带来C++异常处理的知识,敬请期待~

 最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

 最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

赞(0)
未经允许不得转载:网硕互联帮助中心 » 【知识讲解】 C语言函数指针的摇身一变--C++11包装器的介绍与其在算法题目中的使用
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!