C++STL之算法 | 函数对象

函数对象的概念

重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载“()”操作符,使得类对象可以像函数那样调用。

注意:
1.函数对象(仿函数)是一个类,不是一个函数。
2.函数对象(仿函数)重载了”() ”操作符使得它可以像函数一样调用。

假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为“一元仿函数”(unary functor);相反,如果重载的operator()要求获取两个参数,就将这个类称为“二元仿函数”(binary functor)。

函数对象基本概念
函数对象也可以有参数和返回值
函数对象超出函数概念,可以保存函数调用状态
函数对象做参数和返回值

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;

class FuncObject01{
public:
    void operator()(){
        cout << "hello world" << endl;
    }
};

void FuncObject02(){
    cout << "hello world" << endl;
}

//函数对象概念
void test01(){
    
    FuncObject01 fobj;
    fobj();
    FuncObject02();

}

class FuncObject03{
public:
    int operator()(int a, int b){
        return a + b;
    }
};

int FuncObject04(int a,int b){
    return a + b;
}
//函数对象也可以像普通函数一样 具有返回值和参数
void test02(){
    
    FuncObject03 fobj;
    int ret = fobj(10,20);
    cout << "ret :" << ret << endl;

    ret = FuncObject04(10,20);
    cout << "ret :" << ret << endl;

}
//函数对象超出了普通函数的功能,可以具有保存函数调用状态
//例如 我们要统计函数调用次数

class FuncObject05{
public:
    FuncObject05() :count(0){}
    void operator()(){
        cout << "hello world" << endl;
        count++;
    }
    int count;
};

//普通函数要统计调用次数 需要一个全局变量
int g_count = 0;
void FuncObject06(){
    cout << "hello world" << endl;
    g_count++;
}
void test03(){

    FuncObject06();
    FuncObject06();
    cout << "函数调用次数:" << g_count << endl;

    //使用函数对象 不需要使用全局变量
    FuncObject05 fobj;
    fobj();
    fobj();
    fobj();

    cout << "函数调用次数:" << fobj.count << endl;

}

//函数对象做参数和返回值
class print{
public:
    print() :count(0){}
    void operator()(const int& val){
        cout << val << " ";
        count++;
    }
    int count;
};
void test04(){
    
    vector v;
    v.push_back(1);
    v.push_back(3);
    v.push_back(5);
    v.push_back(2);

    //通过for_each算法 遍历容器元素
    print myprint;
    //函数对象做返回值和参数
    myprint = for_each(v.begin(), v.end(), myprint);
    cout << endl;

    cout << "函数对象调用次数:" << myprint.count << endl;
}

int main(){
    test01();
    test02();
    test03();
    test04();
    
    system("pause");
    return EXIT_SUCCESS;
}

谓词概念

谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受一个参数,那么叫做一元谓词,如果接受两个参数,那么叫做二元谓词,谓词可作为一个判断式。

struct myfuncobj01{
      bool operator()(int v)const{}  //接受一个参数,并且返回值为Bool 即一元谓词
}
bool compare01(int v); //同样是叫做一元谓词

struct myfuncobj02{
      bool operator()(int v1,int v2)const{}  //接受两个参数,返回值为Bool 即二元谓词
}
bool compare02(int v1,int v2); //同样是叫做二元谓词

上面想将struct改为class也行,不过要在重载函数前加public,因为class内默认是private

内建函数对象

STL内建了一些函数对象。分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能。
使用内建函数对象,需要引入头文件 #include

6个算数类函数对象,除了negate是一元运算,其他都是二元运算。

template T plus//加法仿函数
template T minute//减法仿函数
template T multiplies//乘法仿函数
template T divides//除法仿函数
template T modulus//取模仿函数
template T negate//取反仿函数

6个关系运算类函数对象,每一种都是二元运算。

template bool equal_to//等于
template bool not_equal_to//不等于
template bool greater//大于
template bool greater_equal//大于等于
template bool less//小于
template bool less_equal//小于等于

逻辑运算类运算函数,not为一元运算,其余为二元运算。

template bool logical_and//逻辑与
template bool logical_or//逻辑或
template bool logical_not//逻辑非

使用例子:

//使用内建函数对象声明一个对象
plus myPlus;
cout << myPlus(5, 3) << endl;
//使用匿名临时对象
cout << plus()(5, 6) << endl;
sort排序使用预定义函数对象进行排序。
count_if equal_to 参数绑定

函数对象适配器

函数对象适配器是完成一些配接工作,这些配接包括绑定(bind),否(negate),以及对一般函数或成员函数的修饰,使其成为函数对象,重点掌握函数对象适配器(红色字体):

bind1st :将参数绑定为函数对象的第一个参数
bind2nd : 将参数绑定为函数对象的第二个参数
not1 : 对一元函数对象取反
not2 : 对二元函数对象取反
ptr_fun : 将普通函数修饰成函数对象
mem_fun : 将成员函数修饰成函数对象,当容器内是对象指针时用这个,这是与mem_fun_ref的区别
mem_fun_ref : 将成员函数修饰成函数对象

预定义函数对象
仿函数适配器bind1st bind2nd
仿函数适配器not1 not2
仿函数适配器 ptr_fun
成员函数适配器 mem_fun mem_fun_ref

#define _CRT_SECURE_NO_WARNINGS

#include
#include
#include
#include
#include
using namespace std;

/*
    template T plus//加法仿函数
    template T minute//减法仿函数
    template T multiplies//乘法仿函数
    template T divides//除法仿函数
    template T modulus//取模仿函数
    template T negate//取反仿函数
*/
//预定义函数对象
class print{
public:
    void operator()(int v){
        cout << v << "    ";
    }
};
void test01(){
    
    plus myplus; //实例化一个对象
    int ret = myplus(10, 20);
    cout << "ret : " << ret << endl;

    cout << plus()(30, 40) << endl;

    vector v1, v2, v3;
    for (int i = 0; i < 10;i++){
        v1.push_back(i);
        v2.push_back(i + 1);
    }

    v3.resize(v1.size());
    transform(v1.begin(),v1.end(),v2.begin(),v3.begin(),plus());

    for_each(v1.begin(), v1.end(), print());
    cout << endl;

    for_each(v2.begin(), v2.end(), print());
    cout << endl;

    for_each(v3.begin(), v3.end(), print());
    cout << endl;

}

//函数适配器bind1st bind2nd
//现在我有这个需求 在遍历容器的时候,我希望将容器中的值全部加上100之后显示出来,怎么做哇?
struct myprint : public binary_function{   //二元函数对象 所以需要继承 binary_fucntion<参数类型,参数类型,返回值类型>
    void operator()(int v1 ,int v2) const{
        cout << v1 + v2 << " ";
    }
};
void test02(){
    
    vector v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);

    //我们直接给函数对象绑定参数 编译阶段就会报错
    //for_each(v.begin(), v.end(), bind2nd(myprint(),100));
    //如果我们想使用绑定适配器,需要我们自己的函数对象继承binary_function 或者 unary_function
    //根据我们函数对象是一元函数对象 还是二元函数对象
    for_each(v.begin(), v.end(), bind2nd(myprint(), 100));
    cout << endl;

    //总结:  bind1st和bind2nd区别?
    //bind1st : 将参数绑定为函数对象的第一个参数
    //bind2nd : 将参数绑定为函数对象的第二个参数
    //bind1st bind2nd将二元函数对象转为一元函数对象

}

//函数对象适配器 not1 not2
struct myprint02 {
    void operator()(int v1) const{
        cout << v1 << " ";
    }
};
void test03(){

    vector v;
    v.push_back(2);
    v.push_back(1);
    v.push_back(5);
    v.push_back(4);
    
    vector::iterator it =  find_if(v.begin(), v.end(), not1(bind2nd(less_equal(), 2)));
    cout << "it:" << *it << endl;
    sort(v.begin(),v.end(),not2(greater()));

    for_each(v.begin(), v.end(), myprint02());
    cout << endl;

    //not1 对一元函数对象取反
    //not2 对二元函数对象取反
}

//如何给一个普通函数使用绑定适配器(bind1st bind2nd)绑定一个参数?(拓展)
//ptr_fun
void myprint04(int v1,int v2){
    cout << v1 + v2 << " ";
}
void test04(){
    
    vector v;
    v.push_back(2);
    v.push_back(1);
    v.push_back(5);
    v.push_back(4);


    //1 将普通函数适配成函数对象
    //2 然后通过绑定器绑定参数
    for_each(v.begin(), v.end(), bind2nd(ptr_fun(myprint04),100));
    cout << endl;

    //总结: ptr_fun 将普通函数转变为函数对象
}

//mem_fun mem_fun_ref
//如果我们容器中存储的是对象或者对象指针,如果能指定某个成员函数处理成员数据。
class student{
public:
    student(string name, int age) :name(name), age(age){}
    void print(){
        cout << "name:" << name << " age:" << age << endl;;
    }
    void print2(int a){
        cout << "name:" << name << " age:" << age << " a:" << a << endl;
    }
    int age;
    string name;
};
void test05(){
    

    //mem_fun : 如果存储的是对象指针,需要使用mem_fun
    vector v;
    student* s1 = new student("zhaosi",10);
    student* s2 = new student("liuneng", 20);
    student* s3 = new student("shenyang", 30);
    student* s4 = new student("xiaobao", 40);

    v.push_back(s1);
    v.push_back(s2);
    v.push_back(s3);
    v.push_back(s4);

    for_each(v.begin(), v.end(), mem_fun(&student::print));
    cout << "-----------------------------" << endl;

    //mem_fun_ref : 如果存储的是对象,需要使用mem_fun_ref

    vector v2;
    v2.push_back(student("zhaosi",50));
    v2.push_back(student("liuneng", 60));
    v2.push_back(student("shenyang", 70));
    v2.push_back(student("xiaobao", 80));

    for_each(v2.begin(), v2.end(), mem_fun_ref(&student::print));

}


int main(){

    //test01();
    //test02();
    //test03();
    //test04();
    test05();

    system("pause");
    return EXIT_SUCCESS;
}

如果希望函数对象适配器能对我们自己编写的函数对象有效,我们需要根据我们的函数对象类型继承STL的父类对象。
如果你本身是二元函数对象 需要继承
二元函数继承:public binary_function<参数类型,参数类型,返回类型>
一元函数继承:public unary_function<参数类型,返回类型>

如果是普通函数、成员函数转化成的函数对象就不需要继承

你可能感兴趣的:(C++STL之算法 | 函数对象)