Skip to content

一.仿函数基本概念

在C++中,仿函数(Functor)是一个重要的概念,它是通过类的形式实现的,使得对象可以像函数一样被调用。具体来说,一个仿函数是任何可以使用函数调用运算符 () 的类的对象。这种功能是通过在类中重载 operator() 来实现的。

仿函数的用途广泛,包括作为排序准则传递给排序函数,或者作为算法的自定义操作。下面是一个简单的示例来说明如何定义和使用一个仿函数:

定义一个简单的仿函数

c++
#include <iostream>

// 定义一个仿函数类
class Increment {
public:
    // 构造函数
    explicit Increment(int start) : num(start) {}

    // 重载 () 运算符
    int operator() () {
        return num++;  // 返回当前数值后自增
    }

private:
    int num;
};

int main() {
    Increment inc(10);  // 创建一个从10开始的Increment对象
    std::cout << inc() << std::endl;  // 输出: 10
    std::cout << inc() << std::endl;  // 输出: 11
    return 0;
}

在这个例子中,Increment 类重载了 operator(),使得 Increment 类的对象可以像调用函数一样被调用。对象 inc 被创建后,每次调用 inc() 都会返回一个数值,然后将内部计数增加 1。

仿函数的优势

  • 状态保持: 与普通函数相比,仿函数可以保持状态。在上述例子中,每次调用 inc() 都会根据内部状态 num 改变其行为。
  • 可定制性: 通过在仿函数中加入更多的成员和方法,可以让它们非常灵活和强大。
  • 内联调用: 由于仿函数是通过对象的方法调用实现的,它们经常可以被编译器内联,这可以减少函数调用的开销,优化性能。

仿函数在C++标准库中广泛应用,尤其是在算法(如 sortfor_each 等)中,它们通过模板参数接受仿函数,从而提供灵活的操作方式。

二.谓词

在 C++ 标准模板库(STL)中,谓词是指能返回布尔值的函数或函数对象(例如仿函数)。它们常用于算法中,比如排序、搜索、删除元素等。根据参数的数量,谓词可以分为一元谓词和二元谓词:

一元谓词(Unary Predicate)

一元谓词是一个接受单个参数并返回布尔值的函数或函数对象。它们经常用在那些只需要单一数据点决定操作的算法中,比如 std::find_ifstd::remove_if

cpp
#include <iostream>
#include <vector>
#include <algorithm>

// 定义一个一元谓词函数对象,用来判断整数是否为奇数
struct IsOdd {
    bool operator()(int x) const {
        return x % 2 != 0;
    }
};

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};

    // 使用一元谓词从向量中找到第一个奇数
    auto it = std::find_if(nums.begin(), nums.end(), IsOdd());
    if (it != nums.end()) {
        std::cout << "The first odd number is: " << *it << std::endl;
    }

    return 0;
}

在这个例子中,IsOdd 是一个判断给定整数是否为奇数的一元谓词。

二元谓词(Binary Predicate)

二元谓词是一个接受两个参数并返回布尔值的函数或函数对象。它们用于需要比较两个数据点的算法,例如 std::sortstd::unique

示例:

cpp
#include <iostream>
#include <vector>
#include <algorithm>

// 定义一个二元谓词函数对象,用于比较两个整数的大小
struct LessThan {
    bool operator()(int a, int b) const {
        return a < b;
    }
};

int main() {
    std::vector<int> nums = {5, 3, 1, 4, 2};

    // 使用二元谓词对向量进行排序
    std::sort(nums.begin(), nums.end(), LessThan());

    std::cout << "Sorted numbers: ";
    for (int num : nums) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这个例子中,LessThan 是一个用于比较两个整数的二元谓词。

应用

谓词在 STL 中的应用非常广泛,因为它们提供了一种灵活的方式来控制算法的行为。通过定义自己的一元或二元谓词,开发者可以对 STL 算法进行高度定制,以满足特定的需求。这种方式优化了代码的重用性和扩展性,并保持了代码的清晰和效率。

三.内建函数

在 C++ 标准模板库(STL)中,内建函数对象是一些预定义的函数对象,也称为函数适配器。这些对象封装了基本的算术运算、逻辑运算和比较操作。它们被定义在头文件 <functional> 中,并可以被用于各种算法,特别是那些需要操作或比较元素的算法。使用这些内建函数对象可以减少自己定义函数对象的需要,并使代码更简洁、更标准化。

主要的内建函数对象

以下是一些常见的内建函数对象类别,每个类别中都包含了多个函数对象:

算术类函数对象

  • std::plus<>:执行加法。
  • std::minus<>:执行减法。
  • std::multiplies<>:执行乘法。
  • std::divides<>:执行除法。
  • std::modulus<>:计算模(仅限整数)。
  • std::negate<>:返回数值的相反数。

比较类函数对象

  • std::equal_to<>:检查等价性(==)。
  • std::not_equal_to<>:检查非等价性(!=)。
  • std::greater<>:执行大于比较(>)。
  • std::less<>:执行小于比较(<)。
  • std::greater_equal<>:执行大于等于比较(>=)。
  • std::less_equal<>:执行小于等于比较(<=)。

逻辑类函数对象

  • std::logical_and<>:逻辑与(&&)。
  • std::logical_or<>:逻辑或(||)。
  • std::logical_not<>:逻辑非(!)。

示例:使用内建函数对象

下面是如何在 C++ 中使用一些这些内建函数对象的示例:

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5};

    // 使用内建函数对象进行算术操作
    std::transform(data.begin(), data.end(), data.begin(), std::negate<int>());

    // 输出变换后的数据
    for (int n : data) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    // 使用内建函数对象作为比较函数
    bool all_positive = std::all_of(data.begin(), data.end(), std::bind(std::greater<int>(), std::placeholders::_1, 0));
    std::cout << "All elements are positive: " << std::boolalpha << all_positive << std::endl;

    return 0;
}

在这个示例中,我们使用了 std::negate<> 来将一个向量中的每个元素变成它的相反数,然后使用 std::greater<> 来检查变换后的向量是否所有元素都大于0。

总结

内建函数对象在 STL 中的应用广泛,它们为常见的算术运算、逻辑运算和比较操作提供了方便的封装。这使得在使用 STL 算法时能够更加方便地执行复杂操作,同时代码更加简洁和易于理解。使用这些预定义的函数对象,可以有效地减少代码量并增加代码的可读性和可维护性。

评论区

欢迎留言、补充或勘误。

xiaoba.blog