首页 > 其他分享 >仿函数

仿函数

时间:2023-03-08 23:14:48浏览次数:31  
标签:return struct val vec operator 函数

仿函数

1.什么是仿函数

1.定义和作用

仿函数是一种重载了函数调用运算符(operator())的类或结构体,它可以像函数一样被调用。仿函数可以在很多STL算法中使用,例如sort、for_each、transform等,可以自定义排序规则、操作、条件等等。通过仿函数,C++程序员可以更加灵活地实现自己的算法。

与普通函数不同,仿函数可以保存状态,因此在使用仿函数时可以灵活地传递参数并进行计算,非常适用于一些复杂的算法和数据结构的实现。

2.仿函数与函数指针的区别

在C++中,函数指针可以作为参数传递和返回值,但是函数指针只能指向函数,无法指向类成员函数和lambda表达式。而仿函数可以作为一种通用的函数封装,可以指向函数、类成员函数以及lambda表达式,并且可以保存状态。因此,仿函数比函数指针更加灵活和可扩展。

3.仿函数的调用方式

仿函数可以像函数一样被调用

class MyFunctor {
public:
    void operator() (int i) {
        cout << i << endl;
    }
};
MyFunctor myFunctor;
myFunctor(123);
//结果
//123

在STL算法中,仿函数也可以作为函数参数传递

vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(vec.begin(), vec.end(), greater<int>());
for_each(vec.begin(), vec.end(), MyFunctor());

其中,greater()是一个内置的仿函数对象,用于实现降序排列。

2.仿函数的分类

1.一元仿函数和二元仿函数

一元仿函数是指只有一个参数的仿函数

template <typename T>
struct MyFunctor1 {
    void operator() (T val) {
        cout << val << endl;
    }
};
template <typename T>
struct MyFunctor2 {
    T operator() (T val) {
        return val * val;
    }
};

二元仿函数是指有两个参数的仿函数

template <typename T>
struct MyFunctor3 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};

在STL算法中,一元仿函数和二元仿函数通常用于排序、查找、遍历等操作。

2.函数对象与谓词

  • 函数对象:返回值为任意类型的仿函数,例如std::plus,std::minus
  • 谓词:返回值为bool类型的仿函数,例如std::less,std::greater

3.函数适配器

函数适配器是一种特殊的仿函数,它用于将一个仿函数适配到另一个仿函数或函数对象上。STL中常用的函数适配器有:bind1st、bind2nd、not1、not2、logical_and、logical_or等

template <typename T>
struct MyFunctor4 {
    bool operator() (T val) {
        return val > 0;
    }
};
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
count_if(vec.begin(), vec.end(), not1(MyFunctor4<int>()));
//结果
//3

其中,not1是一个函数适配器,它将MyFunctor4适配成一个返回相反值的仿函数对象,从而统计出vec中小于等于0的元素个数。

3.仿函数的实现

1.重载函数调用运算符

仿函数的实现首先要重载函数调用运算符(operator()),并根据需要定义参数和返回值

template <typename T>
struct MyFunctor1 {
    void operator() (T val) {
        cout << val << endl;
    }
};
template <typename T>
struct MyFunctor2 {
    T operator() (T val) {
        return val * val;
    }
};

其中,MyFunctor1是一个一元仿函数,用于输出参数值,而MyFunctor2是一个一元仿函数,用于计算参数的平方。

2.使用模板类

仿函数通常是一个模板类,可以支持不同的参数类型

template <typename T>
struct MyFunctor3 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};

其中,MyFunctor3是一个二元仿函数,用于比较两个参数的大小。

3.使用函数适配器

仿函数也可以使用函数适配器来实现。例如,使用bind1st函数适配器将一个二元仿函数适配成一个一元仿函数

template <typename T>
struct MyFunctor4 {
    bool operator() (T val1, T val2) {
        return val1 < val2;
    }
};
MyFunctor4<int> myFunctor;
bind1st(myFunctor, 10)(5);
//结果
//false

其中,bind1st将myFunctor适配成一个只有一个参数的仿函数,第一个参数被绑定为10,然后用5调用这个仿函数,返回false。

4.实现一个简单的加法仿函数

class AddFunctor{
public:
    AddFunctor(int n):m_n(n){}
    int operator()(int x) const{
        return x + m_n;
    }
private:
    int m_n;
}

在上面的代码中,我们定义了一个AddFunctor类,它带有一个整型参数n,表示每次调用要加上的值。在调用运算符()时,返回x加上m_n的结果。

使用AddFunctor类,可以有如下示例

int main()
{
	AddFunctor add(3);
    int result = add(5);	//结果为8
    return 0;
}

4.仿函数的应用

在STL中,许多算法都需要使用仿函数。例如:

  • std::sort():对给定的序列进行排序,需要提供一个比较函数,用于指定排序的规则
  • std::for_each():对给定的序列中的每个元素执行指定的操作,需要提供一个函数对象,用于指定操作
  • std::find_if():在给定的序列中查找符合指定条件的第一个元素,需要提供一个谓词,用于指定条件

通过使用仿函数,我们可以将算法和数据结构解耦,是的算法更加通用和灵活。

5.仿函数的注意事项

在使用仿函数时需要注意以下几点

  • 仿函数的效率问题:由于仿函数在调用时需要进行对象的构造和析构,因此在一些需要频繁调用的场景中,使用仿函数可能会影响算法的效率
  • 仿函数的线程安全性:由于仿函数中可能会保存状态,因此在多线程环境下使用时需要注意线程安全性问题

6.仿函数的优化

1.使用constexpr

在C++11中,可以使用constexpr关键字来声明一个函数或变量是常量表达式,可以在编译时计算。如果一个仿函数的operator()是一个常量表达式,可以使用constexpr来进行优化

template <typename T>
struct MyFunctor1 {
    constexpr T operator() (T val) const {
        return val * val;
    }
};

这样,当使用MyFunctor1时,如果参数是一个常量表达式,那么编译器就可以在编译时计算出结果,从而提高程序的执行效率。

2.使用inline

仿函数可以使用inline关键字来声明为内联函数,从而在编译时将函数体直接嵌入到调用处,避免了函数调用的开销

template <typename T>
struct MyFunctor2 {
    inline T operator() (T val) const {
        return val * val;
    }
};

3.使用lambda表达式

C++11引入了lambda表达式,可以方便地定义一个匿名仿函数。与普通的仿函数相比,使用lambda表达式可以减少代码的冗余,从而提高程序的可读性和维护性

vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
auto count = count_if(vec.begin(), vec.end(), [](int val) { return val > 0; });

其中,lambda表达式[] (int val) { return val > 0; }定义了一个匿名仿函数,用于统计vec中大于0的元素个数。

标签:return,struct,val,vec,operator,函数
From: https://www.cnblogs.com/Vergissmeinnicht-rj/p/17196619.html

相关文章

  • strncmp函数
    用途:strncmp函数用于比较两个字符串的任意等长子段,复杂度O(n)级别格式 Strncmp(s1+x,s2+y,len),其中s1,s2为两个字符串,x和y分别为这两个字符串要比较的起始位置-1,即取从该......
  • 单行函数
    单行函数函数分为内置函数和自定义函数内置函数从实现的功能角度可以分为数值函数、字符串函数、日期和时间函数、流程控制函数、加密与解密函数获取MySQL信息函数、聚......
  • 数据库的数值函数
    1.ceil(x)向上取整2.floor(x)向下取整3.mod(x,y)返回 x/y的模4.rand 返回0-1随机数5.round(x,y) 求参数x的四舍五入的值,保留y位小数    练......
  • (P03)从C到C++:域运算符,new,delet运算符,重载,name managling与extern “C“,带默认参数的函
    文章目录​​1.域运算符​​​​2.new、delete运算符​​​​3.重载​​​​4.namemanagling与extern“C”​​​​5.带默认形参值的函数​​​​6.带默认形参值的函数的......
  • (P05)从C到C++:内联函数,带参数宏,4种强制类型转化
    文章目录​​1.内联函数​​​​2.4种新的类型转换运算符​​1.内联函数当程序执行函数调用时,系统要建立栈空间,保护现场,传递参数以及控制程序执行的转移等等,这些工作需要系......
  • ES6-ES11 ES5构造函数继承
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>对象继......
  • Thinking--函数参数Normalize思想在前端中的应用
    Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想。Normalize标准化:Normalize发组件过程中,为了提高组件的灵活性,我们通常需要支持多种传参格式,如何优雅的控制和组......
  • 学习C语言第三弹:函数(1)
    函数是什么?数学中我们常见到函数的概念。维基百科中对函数的定义:子程序。   ·在计算机科学中,子程序(英语:Subroutine,procedure,function,routine,method,subprogram,callab......
  • 函数
                           return与break区别break只会结束循环,return会结束函数          ......
  • SQL 开窗函数
    一、开窗函数开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化,举例如下:1、over(orderbysalary)按照salary排序进行累计,orderby是......