可调用对象

介绍

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

普通函数

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

#include <iostream>  

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

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

函数指针

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

#include <iostream>  

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

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

函数对象(仿函数)

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

#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 引入的一种轻量级的可调用对象。它们可以捕获周围的变量,并且可以像函数一样被调用。

#include <iostream>  

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

std::function

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

#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()函数的类对象,此时类对象可以当做函数使用,因此称为仿函数

#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 表达式,省去了定义函数的过程,增加开发效率

#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. ()代表 lambda 表达式的参数,函数有参数,lambda 自然也有。

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

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

可调用对象的常见用法

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

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

#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,&可以省略