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

什么是C++中的函数对象?

概念

C++ 中的 函数对象(Function Object),也称为 仿函数(Functor),是指一个可以被当作函数来使用的对象。具体而言,它是一个重载了 operator() 的类或结构体实例。通过这种方式,函数对象可以像普通函数那样被调用,但它们同时也能拥有状态(成员变量)和其他操作。

特点

  • 可调用性:函数对象通过重载 operator() 方法,使得一个类的实例可以被调用,就像函数一样。
#include <iostream>  

class Adder {  
public:  
    // 成员变量,用于存储一个加数  
    int num;  

    // 构造函数  
    Adder(int n) : num(n) {}  

    // 重载 operator()  
    int operator()(int x) {  
        return x + num; // 返回参数与 num 的和  
    }  
};  

int main() {  
    Adder add5(5); // 创建一个 Adder 对象,初始化 num 为 5  

    // 使用函数对象调用  
    std::cout << "5 + 3 = " << add5(3) << std::endl; // 输出:5 + 3 = 8  
    std::cout << "5 + 10 = " << add5(10) << std::endl; // 输出:5 + 10 = 15  

    return 0;  
}

//结果:
		5 + 3 = 8  
		5 + 10 = 15

  • 状态:函数对象可以持有状态(数据成员),可以在调用时根据其内部状态执行不同的操作。
  • 灵活性:函数对象可以拥有人类自定义的逻辑,可以内置复杂的行为。
class  less 
{
    public : 
    less(int  num) : n(num) {} 
    bool operator()(int  value) 
    {
        return  value  < n;
    } 
    private : 
    int n;
};

调用

 
less isLess(10);  
cout << isLess(9) << " " << isLess(12); // 输出 1 0 
//9比10小返回true(1),12比10大返回false(0),
//isless()是一个标准库函数在<cmath>中定义,是用来比较数的大小的,标准用法应该是less(x,y),如果x<y返回true,否则返回false。(上述代码用了函数对象的写法,所以是这样写的),单独用isless函数,还是要用isless(x,y)格式当然还有一种存在有NaN的情况,
//less(x,NaN),无论NaN在那个参数上,都返回false,因为NaN 与任何值的比较结果都是未定义的。

定义

class Adder {  
public:  
    // 构造函数,用于初始化加数  
    Adder(int value) : value(value) {}  

    // 重载 operator(),使对象可以被调用  
    int operator()(int x) const {  
        return x + value;  
    }  

private:  
    int value; // 存储用于加法的值  
};

使用

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

int main() {  
    // 创建函数对象  
    Adder adder(10);  

    // 使用函数对象  
    std::cout << "Adding 5 and 10: " << adder(5) << std::endl; // 输出 15  

    // 在 STL 算法中使用函数对象  
    std::vector<int> numbers = {1, 2, 3, 4, 5};  
    std::transform(numbers.begin(), numbers.end(), numbers.begin(), adder);  

    std::cout << "Transformed numbers: ";  
    for (const auto& n : numbers) {  
        std::cout << n << " "; // 输出 11 12 13 14 15  
    }  
    std::cout << std::endl;  

    return 0;  
}

当然函数对象也有弱势的地方,涉及到指针的情况就无能为力了。不过使用模板就可以解决,即接受函数指针,也能接受函数对象。

template<typename FUNC> //许函数接受任意类型的回调函数 FUNC,使得该函数可以在不同的条件下使用。
int count_n(int* array, int size, FUNC func) //第一个参数一个指向整数数组的指针。第二个数组的大小。第三个用于判断条件的函数。
{  
int count = 0;  
for(int i = 0; i < size; ++i)  
if(func(array[i]))  //检查当前数组元素是否满足条件。
count ++;  //如果满足条件,数器 count 增加 1。
return count;  
} 

使用时

const int SIZE = 5;  
int array[SIZE] = { 50, 30, 9, 7, 20};  
cout << count_n(array, SIZE, less(10)); // 2  条件时小于10的元素 
bool less10(int v)  
{  
return v < 10;  
}  
cout << count_n(array, SIZE, less10); // 2 

这是统计数组中符合条件的元素个数,

适用场景

  • STL 算法:函数对象常被用于算法库(如 std::sort, std::transform 等)中,因为它们可以携带状态和逻辑。
    • 函数对象能够持有状态,这意味着它们可以存储成员变量并在调用时使用这些变量。这对于需要在多个函数调用间保持信息的情况非常有用
    • 数对象可以将复杂的逻辑封装在其 operator() 方法中。这意味着你可以将多个操作结合成一个对象,从而达到更好的可读性和复用性。
#include <vector>  
#include <algorithm>  
#include <iostream>  

class MultiplyBy {  
public:  
    MultiplyBy(int factor) : factor(factor) {}  
    
    int operator()(int x) const {  
        return x * factor; // 将输入的值乘以因子  
    }  

private:  
    int factor; // 存储乘法因子  
};  

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

    // 使用函数对象进行 transform 操作  
    std::transform(numbers.begin(), numbers.end(), std::back_inserter(results), MultiplyBy(2));  

    // 输出结果  
    for (int result : results) {  
        std::cout << result << " "; // 输出:2 4 6 8 10  
    }  
    std::cout << std::endl;  

    return 0;  
}
  • 代替普通函数:当需要传递额外状态或参数时,函数对象比普通函数更为灵活。
    • 需要共享状态:如果多个函数需要共享一些状态,比如计数器、阈值等,使用普通函数可能要求每次调用都手动传递这些额外的信息。
    • 复杂的参数管理:当需要根据不同条件动态修改参数时,普通函数的设计将变得复杂且不方便。
      使用普通函数
#include <iostream>  
#include <vector>  
#include <algorithm>  

void increment(int& value, int incrementBy) {  
    value += incrementBy; // 增加值  
}  

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

    // 使用普通函数进行增加操作  
    for (int& num : numbers) {  
        increment(num, incrementValue); // 每次都需要传递 incrementValue  
    }  

    // 输出结果  
    for (const int& num : numbers) {  
        std::cout << num << " "; // 输出:11 12 13 14 15  
    }  
    std::cout << std::endl;  

    return 0;  
}

在上面的代码中,我们需要在每次调用 increment 函数时都传递 incrementValue,这将代码变得冗长且不灵活。
使用函数对象

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

class Incrementer {  
public:  
    Incrementer(int incrementBy) : increment(incrementBy) {}  

    void operator()(int& value) const {  
        value += increment; // 增加值  
    }  

private:  
    int increment; // 存储增量值  
};  

int main() {  
    std::vector<int> numbers = {1, 2, 3, 4, 5};  
    Incrementer incrementValue(10); // 创建函数对象并携带状态  

    // 使用函数对象进行增加操作  
    std::for_each(numbers.begin(), numbers.end(), incrementValue); // 不需要显式传递  

    // 输出结果  
    for (const int& num : numbers) {  
        std::cout << num << " "; // 输出:11 12 13 14 15  
    }  
    std::cout << std::endl;  

    return 0;  
}

在这个例子中,我们定义了一个 Incrementer 类作为函数对象,该类持有一个增量值,而不是每次调用时都需要传递它。这样,代码变得更加简洁明了,且能够很好地封装状态。

  • 自定义排序:可以用函数对象实现自定义的比较规则,以便在 STL 容器中进行排序或查找操作。
    • 定义自定义结构体
#include <iostream>  
#include <vector>  
#include <algorithm>  
#include <string>
struct Person {  
    std::string name;  
    int age;  

    Person(const std::string& name, int age) : name(name), age(age) {}  
};
  • 定义比较函数对象
    • 接下来,我们定义一个函数对象 CompareByAge,用于根据年龄对 Person 对象进行排序。
class CompareByAge {  
public:  
    bool operator()(const Person& a, const Person& b) const {  
        return a.age < b.age; // 按年龄升序排序  
    }  
};
  • 使用函数对象进行排序
    • 现在我们可以创建一个 std::vector 对象,并使用 std::sort 结合我们的比较函数对象来进行排序。
int main() {  
    std::vector<Person> people = {  
        Person("Alice", 30),  
        Person("Bob", 25),  
        Person("Charlie", 35)  
    };  

    // 使用自定义的比较对象进行排序  
    std::sort(people.begin(), people.end(), CompareByAge());  

    // 输出排序后的结果  
    std::cout << "Sorted by age:\n";  
    for (const auto& person : people) {  
        std::cout << person.name << ", Age: " << person.age << std::endl;  
    }  

    return 0;  
}
  • 输出结果
Sorted by age:  
Bob, Age: 25  
Alice, Age: 30  
Charlie, Age: 35

扩展:自定义多重排序
以下是一个示例,展示如何按年龄和姓名进行排序

class CompareByAgeAndName {  
public:  
    bool operator()(const Person& a, const Person& b) const {  
        if (a.age == b.age) {  
            return a.name < b.name; // 如果年龄相同,则按姓名排序  
        }  
        return a.age < b.age; // 按年龄排序  
    }  
};  

// 在 main 函数中使用这个比较对象:  

int main() {  
    std::vector<Person> people = {  
        Person("Alice", 30),  
        Person("Bob", 25),  
        Person("Charlie", 25),  
        Person("David", 30)  
    };  

    std::sort(people.begin(), people.end(), CompareByAgeAndName());  

    std::cout << "Sorted by age and name:\n";  
    for (const auto& person : people) {  
        std::cout << person.name << ", Age: " << person.age << std::endl;  
    }  

    return 0;  
}

http://www.kler.cn/a/414987.html

相关文章:

  • uniapp定义new plus.nativeObj.View实现APP端全局弹窗
  • 详解登录MySQL时出现SSL connection error: unknown error number错误
  • 企业后端多租户管理平台
  • 抓包之OSI七层模型以及TCPIP四层模型
  • deepin 安装 chrome 浏览器
  • 详谈面试题:Vue、React为什么使用虚拟DOM
  • 【二分查找】力扣 34. 在排序数组中查找元素的第一个和最后一个位置
  • 鸿蒙多线程应用-taskPool
  • spark3.x之后时间格式数据偶发报错org.apache.spark.SparkUpgradeException
  • Linux中网络文件系统nfs使用
  • S4 UPA of AA :新资产会计概览
  • 如何使用PHP爬虫获取店铺详情:一篇详尽指南
  • 初识 Django
  • 2024第六次随堂测验参考答案
  • leetcode 208. 实现 Trie (前缀树)
  • pico-sdk(八)-程序架构之自定义预处理变量
  • 【opencv-python】的cv2.imdecode()与cv2.imencode()
  • 力扣--LCR 148.验证图书取出顺序
  • 二维码有哪些网络安全风险隐患?
  • 【C语言篇】探索 C 语言结构体:从基础语法到数据组织的初体验
  • 力扣,88. 合并两个有序数组
  • [2024年3月10日]第15届蓝桥杯青少组stema选拔赛C++中高级(第二子卷、编程题(1))
  • 项目整合logback日志打印线程id
  • GraphRAG访问模式和知识图谱建模
  • HarmonyOS-初级(一)
  • 【ANC系统】主动噪声控制系统结构分类