C++仿函数的概念优点和使用

 更新时间:2025年04月05日 11:21:21   作者:禁止默  
这篇文章主要介绍了C++仿函数的概念优点和使用,在C++中,仿函数是指重载了operator的类或结构体的对象,它们的行为类似于普通函数,因此可以像函数一样被调用,需要的朋友可以参考下

前言

在 C++ 中,仿函数(Functor) 是指重载了 operator() 的类或结构体的对象,它们的行为类似于普通函数,因此可以像函数一样被调用。仿函数在 STL 算法、回调机制、函数适配器等场景中有着广泛的应用。本文将深入探讨仿函数的概念、优点、使用方式,并结合具体示例进行详细解析。

1. 为什么需要仿函数

在 C++ 中,我们可以用普通函数或 std::function(C++11 引入)来定义可调用对象,但仿函数相比之下有以下优势:

  • 状态存储:普通函数无法存储状态,而仿函数可以在对象内部维护状态,例如计数器、阈值等。
  • 性能优化:由于仿函数是类的实例,可以通过内联优化减少函数调用的开销。
  • 与 STL 兼容:STL 容器和算法广泛使用仿函数,如 std::sort() 可接受仿函数作为自定义排序规则。

2. 仿函数的基本用法

要定义一个仿函数,需要在类或结构体中重载 operator(),示例如下:

#include <iostream>
// 定义仿函数类
struct Add {
    int operator()(int a, int b) {
        return a + b;
    }
};
int main() {
    Add add; // 创建仿函数对象
    std::cout << "3 + 5 = " << add(3, 5) << std::endl; // 像函数一样调用
    return 0;
}

解析

  • operator() 使 Add 对象 add 变成可调用对象,类似于普通函数 add(3, 5)
  • operator() 可以接受参数,并返回计算结果。

3. 具有状态的仿函数

仿函数可以存储状态,使其在多个调用间保持数据。例如,创建一个计算调用次数的仿函数:

#include <iostream>
class Counter {
private:
    int count;
public:
    Counter() : count(0) {}  // 初始化计数器为 0
    int operator()(int value) {
        count++;
        return count * value; // 使用 count 影响计算结果
    }
    int getCount() const { return count; }
};
int main() {
    Counter counter;
    std::cout << counter(10) << std::endl; // 第 1 次调用
    std::cout << counter(10) << std::endl; // 第 2 次调用
    std::cout << "调用次数:" << counter.getCount() << std::endl;
    return 0;
}

解析

  • count 作为成员变量存储状态,每次调用 operator() 都会递增 count
  • 这在 STL 算法、回调机制等场景非常有用。

4. STL 算法中的仿函数

STL 算法通常需要比较、变换、筛选等规则,这时候自定义仿函数特别有用。例如,自定义排序规则:

#include <iostream>
#include <vector>
#include <algorithm>
// 自定义比较规则(降序)
struct Compare {
    bool operator()(int a, int b) {
        return a > b; // 降序排序
    }
};
int main() {
    std::vector<int> vec = {5, 2, 8, 1, 3};
    std::sort(vec.begin(), vec.end(), Compare()); // 传递仿函数对象
    for (int num : vec) {
        std::cout << num << " ";
    }
    return 0;
}

解析

  • std::sort() 默认是升序排序,我们自定义 Compare 作为降序比较规则。
  • std::sort(vec.begin(), vec.end(), Compare()); 传递了 Compare 类型的临时对象作为排序准则。

5. STL 提供的标准仿函数

C++ STL 提供了一些标准仿函数,主要在 <functional> 头文件中,例如:

  • 算术运算仿函数:std::plus<T>std::minus<T>std::multiplies<T>std::divides<T> 等。
  • 关系运算仿函数:std::greater<T>std::less<T>std::equal_to<T> 等。
  • 逻辑运算仿函数:std::logical_and<T>std::logical_or<T> 等。

示例:使用 std::greater<> 进行降序排序:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 包含标准仿函数
int main() {
    std::vector<int> vec = {5, 2, 8, 1, 3};
    std::sort(vec.begin(), vec.end(), std::greater<int>()); // 使用标准仿函数降序排序
    for (int num : vec) {
        std::cout << num << " ";
    }
    return 0;
}

解析

std::greater<int>() 作为 std::sort 的比较函数,与我们自己写的 Compare 作用类似。

6. Lambda 取代仿函数(C++11)

C++11 引入了 Lambda 表达式,使得代码更加简洁,许多仿函数的使用场景可以用 Lambda 代替。例如:

#include <iostream>
#include <vector>
#include <algorithm>
int main() {
    std::vector<int> vec = {5, 2, 8, 1, 3};
    // 使用 Lambda 进行降序排序
    std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; });
    for (int num : vec) {
        std::cout << num << " ";
    }
    return 0;
}

为什么使用 Lambda?

  • 减少代码量:无需单独定义 struct 作为仿函数类。
  • 提高可读性:Lambda 直接在 std::sort() 处定义逻辑,代码更直观。

尽管 Lambda 更简洁,但仿函数在需要存储状态、复用代码、跨多个地方使用时仍然是很好的选择。

7. 总结

特性普通函数Lambda仿函数
是否可存储状态❌ 否⚠️ 仅限闭包捕获✅ 是
是否可复用✅ 是❌ 否(仅局部作用域)✅ 是
性能优化⚠️ 可能无法内联✅ 内联优化✅ 内联优化
适用场景一般计算简单的一次性逻辑STL、回调、复杂逻辑

什么时候选择仿函数

  • 需要存储状态(例如计数器)。
  • 需要复用(多个地方使用相同逻辑)。
  • 需要STL 兼容性(如 std::sort())。
  • 需要高效优化(内联)。

仿函数是 C++ 语言中的重要概念,它使得对象可以像函数一样调用,并在 STL 算法、回调、状态存储等场景中发挥重要作用。虽然 C++11 引入的 Lambda 使代码更加简洁,但仿函数在某些特定场景(如 STL 和状态保持)下仍然不可替代。

以上就是C++仿函数的概念优点和使用的详细内容,更多关于C++仿函数的资料请关注脚本之家其它相关文章!

相关文章

  • 探讨:程序在内存中的分配(常量,局部变量,全局变量,程序代码)问题

    探讨:程序在内存中的分配(常量,局部变量,全局变量,程序代码)问题

    本篇文章是对程序在的内存中分配(常量,局部变量,全局变量,程序代码)的问题进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言高效编程的几招小技巧

    C语言高效编程的几招小技巧

    这篇文章主要介绍了C语言高效编程的几招小技巧,本文讲解了以空间换时间、用数学方法解决问题以及使用位操作等编辑技巧,并给出若干方法和代码实例,需要的朋友可以参考下
    2015-05-05
  • C语言简明清晰讲解结构体

    C语言简明清晰讲解结构体

    C语言结构体(Struct)从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,是由 int、char、float 等基本类型组成的。你可以认为结构体是一种聚合类型
    2022-05-05
  • 深入了解C语言冒泡排序优解

    深入了解C语言冒泡排序优解

    这篇文章主要介绍了C语言冒泡排序法的实现(升序排序法),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • 浅析C++浅拷贝与深拷贝的联系和区别

    浅析C++浅拷贝与深拷贝的联系和区别

    在c++中,深拷贝和浅拷贝也算是一个难点,特别是对于初学者来说,往往在不知道两者区别的情况下而错误的使用了浅拷贝,从而导致了野指针之类的问题,但是又因为缺少理解所以很难定位到问题所在
    2022-09-09
  • c语言指针数组的具体使用

    c语言指针数组的具体使用

    指针数组就是存放指针变量的数组,指针数组的本质是数组,而非指针,本文主要介绍了c语言指针数组的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • C语言实现扫雷小游戏的全过程记录

    C语言实现扫雷小游戏的全过程记录

    这篇文章主要给大家介绍了关于C语言实现扫雷小游戏的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 非常漂亮的新年祝福!C语言实现漂亮的烟花效果

    非常漂亮的新年祝福!C语言实现漂亮的烟花效果

    非常漂亮的新年祝福!这篇文章主要介绍了C语言实现漂亮的烟花效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • C++ std::make_unique和std::make_shared用法小结

    C++ std::make_unique和std::make_shared用法小结

    本文主要介绍了C++ std::make_unique和std::make_shared用法,使用std::make_unique和std::make_shared能够简化动态分配内存和构造对象的过程,提高代码的安全性和可读性,感兴趣的可以了解一下
    2023-11-11
  • C++ primer基础之容器insert

    C++ primer基础之容器insert

    这篇文章主要介绍了C++ primer基础之容器insert的相关资料,需要的朋友可以参考下
    2017-02-02

最新评论