C++——仿函数(函数对象,函数符)

很多STL算法都使用了函数对象——也叫函数符。函数符是可以以函数的方式与()结合使用的任意对象。包括函数名,函数指针,重载()的类对象。

例如,这样的一个类就是函数符

class A
{
private:
    int a;
public:
    A(int x = 20) : a(x) {}
    int operator() (int b)
    {
        return a + b;
    }
};
int main()
{
    A temp;
    cout << temp(10);
    return 0;
}

运行结果

基于函数符的for_each,for_each第三个参数可接受函数指针,但是通用性不强,为了提高通用性,可以传入模板当作函数符。

用模板函数

void show(const T & t)
{
    cout << t << " ";
}
int main()
{
    int num[] = {5,2,1,3,6,9,8,7,20,10};
    list <int> ls(num,num+10);
    //ostream_iterator<int,char> out (cout," ");
    for_each(ls.begin(), ls.end(),show<int>);
    return 0;
}

可以这样做,使用模板类

template <typename T>
class show
{
public:
    void operator()(T & t)
    {
        cout << t << " ";
    }
};
int main()
{
    int num[] = {5,2,1,3,6,9,8,7,20,10};
    list <int> ls(num,num+10);
    //ostream_iterator<int,char> out (cout," ");
    for_each(ls.begin(), ls.end(),show<int>());
    return 0;
}

运行结果

 STL定义的函数符的概念

生成器:不用参数就可以调用的函数

一元函数:使用一个参数就可以调用的函数

二元函数:使用两个参数就可以调用的函数

进一步的

谓词:返回值是bool的一元函数

二元谓词:返回值是bool的二元函数

可以把接受两个参数的模板函数变为接受一个参数的模板类( 重载() )。

例如

template<typename T>
void show(const T & t,int a) {
    cout << t + a << " ";
}
int main()
{
    list <int> ls = {1,2,3,4,5,6,7,8,9,};
    for(auto i = ls.begin();i!=ls.end();i++){
        show(*(i),10);
    }
    return 0;
}

 变为

template<typename T>
class show
{
private:
    T a;
public:
    show(const T & x) : a(x) {}
    void operator()(const T & t)
    {
        cout << t + a << " ";
    }
};
int main()
{
    show<int> s(10);
    list <int> ls = {1,2,3,4,5,6,7,8,9,};
    for(auto i = ls.begin();i!=ls.end();i++){
        s(*(i));
    }
    cout << endl;
    //or
    for_each(ls.begin(), ls.end(),show<int>(5));
    return 0;
}

运行结果

C++预定的一些函数符

例如transform()——他有两种版本

第一个版本接受四个参数,前两个参数是迭代器,表示区间,第三个参数是表示指向复制到哪里的迭代器,最后一个参数是函数符,应用于每一个元素。

例如求每个数的平方根并且复制到输出流

int main()
{
    list <int> ls = {1,2,3,4,5,6,7,8,9,};
    ostream_iterator<double,char> out(cout, " ");
    transform(ls.begin(),ls.end(),out,sqrt<int>);
    return 0;
}

 第二个版本jieshou五个参数,前两个同上,第三个表示第二个容器的起始位置,第四个表示指向复制到哪里的迭代器,第五个是二元函数符,应用于每两个元素。

例如求两个容器的每两个元素和并复制到输出流

template <typename T>
T add(T & t1, T & t2)
{
    return t1 + t2;
}
int main()
{
    //C++预定的函数符sqrt
    list <int> ls = {1,2,3,4,5,6,7,8,9,};
    list <int> ls1 = {9,8,7,6,5,4,3,2,1};
    ostream_iterator<int,char> out(cout, " ");
    transform(ls.begin(),ls.end(),ls1.begin(),out,add<int>);
    return 0;
}

运行结果

 头文件<functional>定义了很多类似的模板函数符。

#include <functional>
+       plus
-       minus
*       multiplies
/       divides
%       modulus
-       negate
==      equal_to
!=      not_equal_to
>       greater
<       less
>=      greater_equal
<=      less_equal
&&      logical_and
||      logical_or
!       logical_not

注意他们本质上是class,只不过重载了()才得以像函数那样。 

自适应函数符和函数适配器

适应就是在处理和分析过程中,根据处理数据的数据特征自动调整处理方法、处理顺序、处理参数、边界条件或约束条件,使其与所 处理数据 的 统计分布 特征、结构特征相适应,以取得最佳的处理效果的过程。

STL有5个相关概念

自适应生成器

自适应一元函数

自适应二元函数

自适应谓词

自适应二元谓词

使函数符成为自适应的原因是它携带了标识参数类型和返回类型的typedef成员。这些成员分别是

result_type, first_argument_type, second_argument_type。

函数符自适应性的意义在于,函数适配器对象  可以使用  函数对象,并认为存在这些typedef的成员。例如, 接受一个 自适应性函数符 参数 的函数 可以使用result_type成员来声明一个于函数的返回类型匹配的变量。

STL提供了只用这些工具的函数适配器类

假设要把容器中的所有元素 x2,可以使用二元函数multiplies, 也可以使用适配写生成一个一元函数。

二元函数

#include <functional>
#include <list>
#include <algorithm>
#include <iterator>
#include <iostream>
using namespace std;

int main()
{
    multiplies<int> mul;
    list <int> ls = {1,2,3,4,5,6,7};
    for(auto i = ls.begin();i != ls.end();i++)
    {
        *(i) = mul(*(i),2);
    }
    ostream_iterator<int,char> out (cout," ");
    copy(ls.begin(),ls.end(),out);
    return 0;
}

这样写显着我们很捞,可以使用函数适配器(本身是一个模板类)

binder1st——可创建其一个对象,该对象可以将一个值与二元函数第一个参数关联从而该对象变为一元函数

binder2nd——同上,但将一个值与二元函数的第二个值关联

因为本质是模板类使用时注意实例化

或者是这两个函数

同上,但是是个函数,不用实例化

bind1st()——返回一个一元函数符对象

bind2nd()——返回一个一元函数符对象

于是可以这样改写代码

int main()
{
    list <int> ls = {1,2,3,4,5,6,7};
    ostream_iterator<int,char> out (cout," ");
    binder1st< multiplies<int> > my_mul (multiplies<int>(),2);
    transform(ls.begin(), ls.end(),out,my_mul);
    return 0;
}

或者 

int main()
{
    list <int> ls = {1,2,3,4,5,6,7};
    ostream_iterator<int,char> out (cout," ");
    transform(ls.begin(), ls.end(),out,bind1st(multiplies<double>(),2));
    return 0;
}

运行结果

两种方法使用时稍微有些不同,函数的方式会更简单一些

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值