C++ 的可调用对象

在学习 使用线程 | 现代C++并发编程教程 的过程中,注意到一个概念,可调用对象,之前没有系统了解,现查询做以下记录:

介绍

在C++中,可调用对象是指任何可以像函数一样被调用的对象。这些对象可以是函数、函数指针、函数对象(仿函数)或 lambda 表达式。

普通函数

普通函数是最基本的可调用对象。你可以直接通过函数名来调用它们。

cpp 复制代码
#include <iostream>  

void sayHello() {  
    std::cout << "Hello, World!" << std::endl;  
}  

int main() {  
    sayHello(); // 调用函数  
    return 0;  
}

函数指针

函数指针是指向函数的指针,可以用来调用指向的函数。它们允许在运行时选择要调用的函数。

cpp 复制代码
#include <iostream>  

void sayHello() {  
    std::cout << "Hello, World!" << std::endl;  
}  

int main() {  
    void (*funcPtr)() = sayHello; // 定义函数指针并指向 sayHello  
    funcPtr(); // 通过函数指针调用函数  
    return 0;  
}

函数对象(仿函数)

函数对象是重载了 operator() 的类的实例。它们可以像普通函数一样被调用,且可以保存状态。

cpp 复制代码
#include <iostream>  

class Functor {  
public:  
    void operator()() const {  
        std::cout << "Hello from Functor!" << std::endl;  
    }  
};  

int main() {  
    Functor f; // 创建函数对象  
    f(); // 调用函数对象  
    return 0;  
}

Lambda 表达式

Lambda 表达式是 C++11 引入的一种轻量级的可调用对象。它们可以捕获周围的变量,并且可以像函数一样被调用。

cpp 复制代码
#include <iostream>  

int main() {  
    auto lambda = []() {  
        std::cout << "Hello from Lambda!" << std::endl;  
    };  
    
    lambda(); // 调用 lambda 表达式  
    return 0;  
}

std::function

std::function 是一个通用的可调用对象包装器,可以存储任何可调用对象,包括普通函数、函数指针、函数对象和 lambda 表达式。它提供了统一的接口来调用这些对象。

cpp 复制代码
#include <iostream>  
#include <functional>  

void sayHello() {  
    std::cout << "Hello from std::function!" << std::endl;  
}  

int main() {  
    std::function<void()> func = sayHello; // 使用 std::function  
    func(); // 调用  
    return 0;  
}

可调用对象的应用

可调用对象在许多场景中非常有用,例如:

  • 回调函数:在事件驱动编程中,常常需要将函数作为参数传递,以便在特定事件发生时调用。
  • STL算法:STL(标准模板库)中的许多算法(如 std::sort)接受可调用对象作为参数,以便在排序或查找时使用自定义的比较逻辑。
  • 多线程:在多线程编程中,可以将可调用对象传递给线程,以便在新线程中执行。

用法

如果一个对象可以使用调用运算符"()",()里面可以放参数,这个对象就是可调用对象

可调用对象分类

(1) 函数:函数自然可以调用()运算符,是最典型的可调用对象

(2) 仿函数:具有operator()函数的类对象,此时类对象可以当做函数使用,因此称为仿函数

cpp 复制代码
#include <iostream>

class Test    // 有operator()函数
{
public:
    void operator()(int i)
    {
        std::cout << i << std::endl;
        std::cout << "hello world" << std::endl;
    }
};

int main() 
{
    Test t;   // t此时就是一个仿函数
    t(20);
    return 0;
}

(3) lambda 表达式:就是匿名函数,普通的函数在使用前需要找个地方将这个函数定义,于是 C++提供了 lambda 表达式,需要函数时直接在需要的地方写一个 lambda 表达式,省去了定义函数的过程,增加开发效率

cpp 复制代码
#include <iostream>

int main() 
{
    [] {
        std::cout << "hello world" << std::endl;
    }();
    return 0;
}

注意:lambda 表达式很重要,现代 C++程序中,lambda 表达式是大量使用的。

lambda 表达式的格式:最少是"[] {}",完整的格式为"[] () ->ret {}"。

lambda 各个组件介绍

  1. \]代表捕获列表:表示 lambda 表达式可以访问前文的哪些变量。 **基本用法** * \[\]表示不捕获任何变量。 * \[=\]:表示按值捕获所有变量。 * \[\&\]:表示按照引用捕获所有变量。 **=,\&也可以混合使用** * \[=, \&i\]:表示变量 i 用引用传递,除 i 的所有变量用值传递。 * \[\&, i\]:表示变量 i 用值传递,除 i 的所有变量用引用传递。 **当然,也可以捕获单独的变量** * \[i\]:表示以值传递的形式捕获 i * \[\&i\]:表示以引用传递的方式捕获 i

  2. ->ret 表示指定 lambda 的返回值,如果不指定,lambda 表达式也会推断出一个返回值的。

  3. {}就是函数体了,和普通函数的函数体功能完全相同

可调用对象的常见用法

(1) 可调用对象作为函数的参数

这里使用函数指针对象举例:

cpp 复制代码
#include <iostream>

void test(int i)
{   
    std::cout << i << std::endl;
    std::cout << "hello world" << std::endl;
}

using pf_type = void(*)(int);   // 函数指针

void myFunc(pf_type pf, int i)  // 可调用对象作为函数的参数
{
    pf(i);
}

int main() 
{
    myFunc(test, 200);
    return 0;
}

参数使用函数指针对象 接收 函数地址,实参&test,&可以省略;

性能对比

在 C++ 中,实现可调用对象的方式有多种,性能各有不同。根据测试,仿函数和 Lambda 表达式的性能最好,其次是函数指针,最差的是 std::function

通过这些方式,可以灵活地创建和使用可调用对象,以满足不同的编程需求。

参考:

可调用对象 - md-book

【c++】可调用对象(Callable Objects)_c++ callable-CSDN博客

三目运算符平均分配数组;

匿名 lambda 表达式的定义 + 立即调用;

相关推荐
tianyuanwo2 小时前
Bash与Sh的诞生背景、底层原理及Linux多Shell解释器兼容性解析
linux·开发语言·bash
千里马-horse2 小时前
Drawing a triangle -- setup -- Base code
c++·vulcan
txinyu的博客2 小时前
unique_ptr shared_ptr weak_ptr的线程安全问题
c++·安全
怦怦蓝2 小时前
IDEA 项目打印日志全攻略:从基础使用到高级配置
java·开发语言·debug
meichaoWen2 小时前
【nodejs】nodejs的一些基础知识
开发语言·前端·javascript
Howrun7772 小时前
虚幻引擎_用户小控件_准星
c++·游戏引擎·虚幻
CoderCodingNo2 小时前
【GESP】C++六级考试大纲知识点梳理, (1) 树的概念与遍历
开发语言·c++
A星空1232 小时前
3519Hisidv500的QT配置
开发语言·qt