当前位置: 首页 > news >正文

C++八股——函数对象

文章目录

    • 一、仿函数
    • 二、Lambda表达式
    • 三、bind
    • 四、function

一、仿函数

仿函数:重载了操作符()的类,也叫函数对象

特征:可以有状态,通过类的成员变量来存储;(有状态的函数对象称之为闭包

样例

class Add {
public:void operator() (int count) {i += count;cout << "i:" << i << endl;}int operator() (int a, int b) {return a + b;}int i = 0; // 状态
};

二、Lambda表达式

Lambda表达式是一种方便创建匿名函数的语法糖,简化函数对象的创建,常用于需要短小逻辑的场景(如 STL 算法)。

语法

[捕获列表](参数列表) mutable -> 返回类型 { 函数体 }

  • 捕获列表:定义如何捕获外部变量,本质是将外部变量转变为类的成员变量
    • [=]:值捕获所有外部变量,只可读,不能修改
    • [&]:引用捕获所有外部变量,可读可写
    • [a, &b]:显示指定,值捕获a,引用捕获b
  • mutable:加上此关键字,允许修改值捕获的变量(值捕获的外部变量成为函数的状态,并不会改变实际的外部变量值)
  • 返回值类型:由于有类型推导,所以可以省略

底层原理

编译器将 lambda 转换为一个仿函数,重载 operator()。例如:

/*
int i = 0;
auto func = [i](int count) mutable -> void {i++;cout << "count:" << count << " i:" << i << endl;
};
*/
class LambdaFun {
public:LambdaFun(int _i) : i(_i) {}void operator() (int count) {i++;cout << "count:" << count << " i:" << i << endl;}
private:int i;
};

例子

std::vector<int> nums = {3, 1, 4};
std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; });
// 降序排序

三、bind

std::bind是用来通过绑定可调用对象以及参数生成新的可调用对象,支持参数顺序调整和部分参数绑定。

用法

#include <functional>
auto new_callable = std::bind(原函数, 绑定参数列表);
  • 占位符std::placeholders::_1, _2, ... 表示未绑定的参数位置

底层原理

编译器将 bind 转换为一个仿函数,重载 operator()。例如:

// auto f = std::bind(add, 10, 20);
class BindFun {
public:BindFun(function<int(int, int)> _fn, int _a, int _b) : fn(_fn), a(_a), b(_b) {}int operator()() const {return fn(a, b);}
private:function<int(int, int)> fn;int a, b;
};// auto f = std::bind(&Add::add, &tmp, 10, std::placeholders::_1);
class BindCFun {
public:// 此处定义了一个类型别名Fn为指向Add类中参数为(int, int),返回值为int的成员函数的指针typedef int (Add::*Fn)(int, int);BindCFun(Fn _fn, Add *_c, int _a) : fn(_fn), c(_c), a(_a) {}int operator()(int b) const {return (c->*fn)(a, b);}
private:Fn fn;Add *c;int a;
};

样例

  • 绑定普通函数

    int add(int a, int b) {return a + b; 
    }
    auto f = std::bind(add, 10, std::placeholders::_1);
    int c = f(20); // c = 30
    
  • 绑定类成员函数

    class Add {
    public:int add(int a, int b) {return a + b;}
    };
    Add tmp;
    auto f = std::bind(&Add::add, &tmp, 10, std::placeholders::_1);
    int c = f(20) // c = 30
    

四、function

std::function是一个抽象了函数参数和返回值的类模板(多态函数包装器)。

用途

把任意函数包装成一个对象,该对象可以保存、传递、复制。

其可以包装:普通函数、类的成员函数和静态成员函数、仿函数、lambda表达式、bind返回的函数对象。

头文件:<functional>

用法:std::function<返回值类型(参数类型列表)> func;

样例

  1. 包装普通函数:

    #include <functional>int add(int a, int b) {return a + b;
    }// 包装普通函数
    std::function<int(int, int)> f_add = add; // f_add1 = &add 效果一样
    int c = f_add(1, 2); // c = 3
    
  2. 包装类静态成员函数:

    class StaticFunc {
    public:static int add(int a, int b) {return a + b;}
    };// 包装类静态成员函数
    std::function<int(int, int)> f_add = &StaticFunc::hello;
    int c = f_add(1, 2); // c = 3
    
  3. 包装态成员函数:

    class Add {
    public:int add(int a, int b) {return a + b;}
    };// 包装类成员函数
    std::function<int(Add *, int, int)> f_add = &Add::hello;
    Add tmp;
    int c = f_add(&tmp, 1, 2); // c = 3
    
  4. 包装仿函数:

    // 包装一中的仿函数Add
    std::function<void(int)> f_add1 = Add();
    std::function<int(int, int)> f_add2 = Add();
    f_add1(1); // 打印 i:1
    f_add1(2); // 打印 i:3, 因为仿函数保存了状态i的值
    int c = f_add2(1, 2); // c = 3
    
  5. 包装Lambda:

    int i = 0;
    auto func = [i](int count) mutable -> void {i++;cout << "count:" << count << " i:" << i << endl;
    };
    // auto 实际为编译器生成的匿名类型(非 std::function)
    // 等效的 std::function 类型为 std::function<void(int)>func(1); // 打印 count:1 i:1
    func(1); // 打印 count:1 i:2
    cout << i << endl; // 打印 0, 因为mutable关键字,所以不会修改外部变量实际的值
    
  6. 包装bind绑定的可调用对象

    三中的样例auto可显示转换为:std::function<int(int)>


总结C++11中function、Lambda、bind之间的关系

function用来描述函数对象的类型;Lambda表达式用来生成函数对象(可以访问外部变量的匿名函数);bind也是用来生成函数对象(函数和参数进行绑定的形式生成)

参考:

  • 【C++面试题】面试官:请简述function,lambda,bind之间的关系_哔哩哔哩_bilibili
  • DeepSeek
http://www.xdnf.cn/news/394273.html

相关文章:

  • 工具篇-扣子空间MCP,一键做游戏,一键成曲
  • C/C++实践(五)C++内存管理:从基础到高阶的系统性实现指南
  • 《从零构建一个简易的IOC容器,理解Spring的核心思想》
  • 命令行解释器中shell、bash和zsh的区别
  • LangChain对话链:打造智能多轮对话机器人
  • C 语言报错 xxx incomplete type xxx
  • CTFd CSRF 校验模块解读
  • 表加字段如何不停机
  • NCCL N卡通信机制
  • 《Effective Python》第1章 Pythonic 思维详解——始终用括号包裹单元素元组
  • 用一张网记住局域网核心概念:从拓扑结构到传输介质的具象化理解
  • 懒人美食帮SpringBoot订餐系统开发实现
  • Linux网络编程day9 libevent库
  • 代码随想录算法训练营第60期第三十二天打卡
  • RAII是什么?
  • 大学之大:东京工业大学2025.5.11
  • 误差函数(Error Function)的推导与物理意义
  • 【电机控制器】PY32MD310K18U7TR——ADC、UART
  • AAAI-2025 | 电子科大类比推理助力精准识别!SPAR:基于自提示类比推理的无人机目标探测技术
  • Java 线程池原理
  • 解决stm32HAL库使用vscode打开,识别不到头文件及uint8_t等问题
  • LOJ 6346 线段树:关于时间 Solution
  • 假如你的项目是springboot+vue怎么解决跨域问题
  • Anaconda环境中conda与pip命令的区别
  • Java--图书管理系统(简易版)
  • 信息安全管理与评估索引
  • 02.three官方示例+编辑器+AI快速学习webgl_animation_skinning_blending
  • C++类和对象--初阶
  • 英伟达微调qwen2.5-32B模型,开源推理模型:OpenCodeReasoning-Nemotron-32B
  • 关于 js:6. 网络与加密模块