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


目录

前言

[Part1. Lambda表达式捕获机制详解](#Part1. Lambda表达式捕获机制详解)

[Part1.1. 值捕获基础特性](#Part1.1. 值捕获基础特性)

[Part1.2. 捕获简写语法](#Part1.2. 捕获简写语法)

[Part1.3. mutable关键字](#Part1.3. mutable关键字)

[Part1.4. lambda底层原理](#Part1.4. lambda底层原理)

[Part2. C++11 类的六大默认成员函数](#Part2. C++11 类的六大默认成员函数)

[Part2.1. 六大默认函数](#Part2.1. 六大默认函数)

[Part2.2. C++11规则](#Part2.2. C++11规则)

[Part2.3. 类型分类](#Part2.3. 类型分类)

[Part2.4. = default关键字](#Part2.4. = default关键字)

[Part3. std::function 函数包装器](#Part3. std::function 函数包装器)

[Part3.1. 作用](#Part3.1. 作用)

[Part3.2. 基础语法](#Part3.2. 基础语法)

[Part3.3. 包装类成员函数](#Part3.3. 包装类成员函数)

[Part3.4. 调用规则](#Part3.4. 调用规则)

[Part3.5. 包装器与逆波兰表达式](#Part3.5. 包装器与逆波兰表达式)

[Part4. std::bind 参数绑定工具](#Part4. std::bind 参数绑定工具)

[Part4.1. 核心能力](#Part4.1. 核心能力)

[Part4.2. 占位符规则](#Part4.2. 占位符规则)

[Part4.3. bind 结合function处理成员函数](#Part4.3. bind 结合function处理成员函数)

[Part5. 结语](#Part5. 结语)


前言

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


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


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

Part1.1. 值捕获基础特性

cpp 复制代码
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关键字

cpp 复制代码
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关键字

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

Part3. std::function 函数包装器

头文件:#include <functional>


Part3.1. 作用

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


Part3.2. 基础语法

cpp 复制代码
// 模板格式: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包装分两种情况:

cpp 复制代码
<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. 包装器与逆波兰表达式


cpp 复制代码
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. 占位符规则

cpp 复制代码
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处理成员函数

cpp 复制代码
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++异常处理的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

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

相关推荐
实验如有神祝女士1 天前
不同参数规模大模型在医学翻译场景的适配差异
论文阅读·人工智能·深度学习·学习·算法·语言模型·论文笔记
windliang1 天前
Claude Code 源码分析(三):一次模型回答如何流进 Agent
前端·算法·ai编程
Kyrie_kk1 天前
Java--BigInteger 超大整数计算
java·算法
汽车网络安全爱好者1 天前
Public Key Infrastructure(二)— 深入理解 X.509 证书:从 RFC 5280 到 OpenSSL 实践
运维·服务器·算法·网络安全·汽车·密码学·可信计算技术
tkevinjd1 天前
力扣131-分割回文串
算法·leetcode·深度优先
BioRunYiXue1 天前
技术干货 | LiP-MS全流程解析:从实验设计到数据分析
大数据·前端·javascript·人工智能·算法·数据挖掘·数据分析
Jasmine_llq1 天前
《P11246 [GESP202409 六级] 小杨和整数拆分》
算法·数学定理版本·时间复杂度n1/2·dp 版本
湿滑路面1 天前
VideoPipe中集成多模态大模型做视频(图片)分析
人工智能·算法·音视频
井川廊咏1 天前
使用typedef封装函数指针新类型
算法
fīɡЙtīиɡ ℡1 天前
分布式ID生成策略
java·分布式·算法