绑定器和函数对象

1.什么时候会用到bind1st 和 bind2nd?

bind1st 和bind2nd 都是绑定二元函数对象成一元函数对象

greater 和 less 都是二元函数

template<typename Container>
void showContainer( Container &con )
{
    typename Container::iterator it = con.begin();
    for(; it != con.end(); ++ it)
    {
        cout << *it << " ";
    }
    cout << endl;
}

int main()
{
    vector<int> vec;
    srand(time(nullptr));
    for(int i = 0; i < 20; ++i)
    {
        vec.push_back(rand() % 100 + 1);
    }
    sort(vec.begin(), vec.end()); //默认小到大排序
    showContainer<vector<int>>(vec);
    //greater 是个二元函数
    sort(vec.begin(), vec.end(),greater<int>()); //从大到小
    showContainer<vector<int>>(vec);

    return 0;
}

 

例子:现在有个需求,按顺序把 50插入进去

find_if需要一个一元函数对象,库里边都是二元函数对象,所以就需要绑定器

绑定器 bind1st + 二元函数对象 = 一元函数对象

bind1st + greater = bool operator()(50,const _Ty& _Right) ==== bind1st(greater<int>(),50)

bind2nd + less = bool operator()(const _Ty& _Left, 50) ==== bind2nd(less<int>(), 50)

auto it1 = find_if(vec.begin(),vec.end(), bind1st(greater<int>(), 50));
auto it  = find_if(vec.begin(),vec.end(), bind2nd(less<int>(), 50));
if (it != vec.end()) vec.insert(it,50);
showContainer<vector<int>>(vec);

2.bind1st  和 bind2nd 的底层实现

bind1st 底层调用的还是greater

template<typename Compare, typename T>
class _mybind1st
{
   public:
        _mybind1st(Compare comp, T val) : _comp(comp), _val(val) {}
        bool operator()(const T& second)
        {
            return _comp(_val,second) ; // 传入的二元函数对象
        }
   private:
      Compare _comp;
      T _val;  
};

bind1st 的接口

template<typename Compare, typename T>
_mybind1st<Compare, T> bind1st(Compare comp, const T& val)
{
    return _mybind1st<Compare, T>(comp, val);
}

find_if 的实现

template<typename Iterator, typename Compare>
Iterator find_if(Iterator first, Iterator last, Compare comp)
{
    for(; first != last; ++ first)
    {
        if(comp(*first))  //comp.operator()(*first)
        {
            return first;
        }
    }
}

3.function函数对象类型应用

function使绑定器、函数对象、lambda表达式(这些只能使用在一条语句中的对象) 保留下来给需要的时候调用

1.用函数类型实例化function

2.通过function调用operator() 函数的时候,需要根据函数类型传入相应的参数

3.function 类 是需要传入一个函数

指针函数和 普通函数:

        指针函数void(*)() :void为返回值,(*)是指针,()是函数传入的值

        普通函数void(): void为返回值,()是函数传入的值


void hello1() { cout << "hello word!" << endl; }

void hello2(string str){ cout << str << endl;}

int sum(int a, int b) { return a + b; }

class Test{
    public:
        void hello(string str)
        { cout << str << endl;}
};

int main()
{
    function<void()> func1 = hello1;
    func1(); //func1.operator()(),然后通过函数指针调用的hello1

    function<void(string)> func2 = hello2;
    func2("string"); //func2.operator()(string str)

    function<int(int, int)> func3 = sum;
    cout << func3(30,50) << endl;

    //lambda表达式
    function<int(int, int)> func4 = [](int a, int b)->int { return a + b; };
    func4(50,60);
    //类函数一经编译,就会多了一个this的指针,就需要多了一个参数
    function<void(Test*, string)> func5 = &Test::hello;
    Test test = Test();
    func5(&test, "call Test::hello"); 
    
    return 0;
    
}

以上代码能看出function有什么用呢?还不如直接调用来的快?看看以下一个例子:

void doShowAllBooks() { cout << "1.查看所有书籍" << endl;}
void doBorrow() { cout << "2.借走书籍" << endl;}
void doBack() { cout << "3.归还书籍" << endl;}
void doQueryBooks() { cout << "4.查询书籍" << endl;}
void doLoginOut() { cout << "5.注销登录" << endl;}

int main()
{
    int choice = 0;
    map<int, function<void()>> actionMap;
    actionMap.insert({3,doBack});
    actionMap.insert({2,doBorrow});
    actionMap.insert({1,doShowAllBooks});
    actionMap.insert({5,doLoginOut});
    actionMap.insert({4,doQueryBooks});
    for(;;)
    {
        cout << "----------" <<endl;
        cout << "1.查看所有书籍" <<endl;
        cout << "2.借走书籍" << endl;
        cout << "3.归还书籍" << endl;
        cout << "4.查询书籍" << endl;
        cout << "5.注销登录" << endl;
        cout << "----------" <<endl;
        cin >> choice;
        actionMap[choice]();
    }
    return 0;
}

4.模板的完全特例化和部分特例化

优先匹配完全特例化版本,没有完全特例化版本就匹配部分特例化,然后再匹配包含全部的的模板类型

//包含全部的版本
template<typename T> 
class Vector
{
    public:
        Vector() { cout << "all" << endl ;}
};

//针对char * 类型提供的完全特例化版本
template<>
class Vector<char*>
{
    public:
        Vector() { cout << "char *" << endl ;}
};

//针对 指针类型提供的部分特例化版本
template<typename Ty>
class Vector<Ty *>
{
    public:
        Vector() { cout << "Ty*" << endl ;}
};

//指针函数指针(有返回值,有两个形参变量)提供的部分特例化
template<typename R, typename A1, typename A2>
class Vector<R(*)(A1, A2)> //定义成一个有返回值的函数,和所有的形参类型都具体化出来
{
    public:
        Vector() { cout << "R(*)(A1, A2)" << endl ;}
};

int main(){
    Vector<int> vec1; //调用包含了所有的 all
    Vector<char *> vec2; //char*
    Vector<int*> vec3;//Ty *
    Vector<int(*)(int, int)> vec4;//R(*)(A1,A2)
    return 0;
}

5.function 底层实现原理

void hello(string str) 
{
    cout << str << endl;
}

例子1:比如要调用hello函数,function的底层实现应该是这个样子的:

//定义一个包含全部的模板类
template<typename Fty>
class myfunction{}; 
//定义部分特例化的模板
template<typename R,typename A1>
class myfunction<R(A1)>
{
    public:
        using PFUNC = R(*)(A1);
        myfunction(PFUNC p):_pfunc(p) {}
        R operator()(A1 arg)
        {
            return _pfunc(arg);
        }
    private:
        PFUNC _pfunc;
};

int main()
{
    myfunction<void(string)> func1 = hello;
    func1("hello");
    return 0;
}

 例子2:实现两个参数的

int sum(int b, int a) 
{
    return a + b;
}
//定义一个包含全部的模板类
template<typename Fty>
class myfunction{}; 
//定义部分特例化的模板
template<typename R,typename A1, typename A2>
class myfunction<R(A1,A2)>
{
    public:
        using PFUNC = R(*)(A1,A2);
        myfunction(PFUNC p):_pfunc(p) {}
        R operator()(A1 arg,A2 arg1)
        {
            return _pfunc(arg,arg1);
        }
    private:
        PFUNC _pfunc;
};

int main()
{
    myfunction<int(int,int)> func1 = sum;
    cout << func1(20,30) <<endl;
    return 0;
}

通过例子1和例子2发现,只要增加一个参数就需要增加一个类模板,那得实现到什么时候,所以需要我们的 “...” 可变参数列表上场了

int sum(int b, int a,int c) 
{
    return a + b + c;
}
//定义一个包含全部的模板类
template<typename Fty>
class myfunction{}; 
//定义部分特例化的模板
template<typename R,typename... A1>
class myfunction<R(A1...)>
{
    public:
        using PFUNC = R(*)(A1...); //把函数保留成指针函数
        myfunction(PFUNC p):_pfunc(p) {}
        R operator()(A1... arg)
        {
            return _pfunc(arg...); //通过运算符重载就调用了函数指针
        }
    private:
        PFUNC _pfunc;
};

int main()
{
    myfunction<int(int,int,int)> func1 = sum;
    cout << func1(20,30,40) <<endl;
    return 0;
}

6.bind 和 function 实现线程池

bind的使用:

        1.返回的结果还是一个函数对象

        2.和function差不多

        3.只能在一条语句上使用,出了作用域就无法使用

#include <iostream>
#include <ctime>
#include <vector>
#include <functional>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
using namespace placeholders;

void hello(string str) { cout << str << endl; }

int Sum(int a, int b) { return a + b; }

class Test { public: int sum(int a, int b) { return a + b; } };

int main()
{
    bind(hello,"hello bind!");
    cout << bind(Sum,10,20)() <<endl;
    cout << bind(&Test::sum,Test(), 20, 30)() << endl;

    //bind 的参数占位符 placeholders::_1 代表有一个参数,最多有_20
    bind(hello,placeholders::_1)("hello");
    //可以使用 using namespace placeholders; 命名空间
    cout << bind(Sum,_1,_2)(20,30) <<endl;

    //使用function保留bind
    function<int(int,int)> b = bind(Sum,_1,_2);
    b(10,80);
    return 0;
}

线程池绑定的例子:

//线程类
class Thread{
    public:
        Thread(function<void()> func) : _func(func) {}
        thread start()
        {
            thread t(_func);
            return t;
        }
    private:
        function<void()> _func;
};

//线程池类
class ThreadPool
{
    public:
        ThreadPool(){}
        ~ThreadPool(){
            for(int i = 0 ; i < _pool.size(); ++i)
            {
                delete _pool[i];
            }
        }
        void startPool(int size)
        {
            for(int i = 0; i < size; ++i)
            {
                //把当前类对象绑定
                _pool.push_back(new Thread(bind(&ThreadPool::runInThread,this,i)));
            }
            for(int i = 0; i < size; ++i)
            {
                _handler.push_back(_pool[i]->start());
            }
            for(thread &t:_handler)
            {
                t.join();
            }
        }
    private:
        vector<Thread*> _pool;
        vector<thread>  _handler;
        //把runInThread这个成员方法充当线程函数
        void runInThread(int id)
        {
            cout << "call runInThread id:" << id << endl;
        }
};

int main()
{
    ThreadPool pool;
    pool.startPool(10);
    return 0;
}

7.lambda表达式的实现原理(函数对象的升级版)

 函数对象的缺点:需要实现一个类的()运算符重载函数,也就必须要定义一个类

class lambda_
{
    public:
        lambda_(int a, int b):_a(a),_b(b) {}
        void operator()()
        {
            cout << " call function " << endl;
        }
    private:
        int _a;
        int _b;
};

lambda表达式的语法:

[]()->int {};

例子:
auto func = []()->void { cout << "hello" << endl; };
func();

[捕获外部变量]:

        [] : 表示不捕获任何外部变量

        [=]:以传值的方式捕获外部的所有变量

        [&]:以引用的方式捕获外部的所有变量

        [this]:捕获外部的this指针

        [=,&a] :捕获外部所有变量以值传递,但是a变量以引用的方式捕获

        [a,b]:以传值的方式捕获变量ab;

        [a,&b]: 相当于 (int a, int &b)

 例子[]:

会发生错误,[]是不捕获任何外部资源,所有a b在表达式里边使用是错误的

int main()
{
    int a = 10; 
    int b = 20;

    auto func = []()->void 
    { 
        int temp = a;
        a = b;
        n = temp;
    }
    func();
    return 0;
}

 例子[=]:

以上例子改为[=],也会发生错误,lambda表达式为const常方法,只能读值不能写值。

        可以给lambda表达式修饰为mutable.    

                相当于给类里边的成员变量都加上了 ----> mutable int a; mutable int b;    这个例子也不能改变外部的值

int main()
{
    int a = 10; 
    int b = 20;

    auto func = [=]()->void mutable
    { 
        int temp = a;
        a = b;
        n = temp;
    }
    func();
    return 0;
}

例子[&]:

外部a 和 b都被改变,因为传的是引用,并不会改变值,所以常方法并没有影响

int main()
{
    int a = 10; 
    int b = 20;

    auto func = [&]()->void 
    { 
        int temp = a;
        a = b;
        n = temp;
    }
    func();
    return 0;
}

如果lambda表达式的返回值不需要,那么"->返回值" 可以省略

lambda常用方式:

int main()
{
    vector<int> vec;
    for(int i = 0;i < 20; ++i)
    {
        vec.push_back(rand() % 100 +1);
    }
    //这样可以直接写lambda表达式,无需记住greater或less的函数名
    sort(vec.begin(), vec.end(), [](int a, int b)->bool { return a > b;});
    for(int val:vec)
    {
        cout << val << " ";
    }
    cout << endl;
    //查找
    auto it = find_if(vec.begin(),vec.end(), [](int a)->bool{ return a < 50;});
    if (it != vec.end()) vec.insert(it, 50);

    //偶数打印
    for_each(vec.begin(),vec.end(), [](int a){ if(a % 2 == 0) cout << a << " "; });

    return 0;
}

8.lambda表达式的应用 #include <functional>

lambda表达式只能使用在语句当中,如果想跨语句使用之前定义好的lambda表达式应该怎么办?用什么类型来表达lambda表达式?

: 是使用function类型来表示函数对象的类型,只要是函数对象类型都可以使用function来表示:bind1st/bind2nd  bind绑定器

int main()
{
    map<int, function<int(int,int)>> caculateMap;
    caculateMap[1] = [](int a, int b)->int { return a + b;};
    caculateMap[2] = [](int a, int b)->int { return a - b;};
    caculateMap[3] = [](int a, int b)->int { return a * b;};
    caculateMap[4] = [](int a, int b)->int { return a / b;};

    cout << caculateMap[1](10,5) << endl;
    cout << caculateMap[2](10,5) << endl;
    cout << caculateMap[3](10,5) << endl;
    cout << caculateMap[4](10,5) << endl;
    return 0;
}

释放文件堆资源:

//智能指针自定义删除器 delete p;  FILE*  fclose(FILE*)
unique_ptr<FILE,function<void(FILE*)>> ptr1(fopen("data.txt", "w"),
     [](FILE *pf){ fclose(pf);} 
    );

优先级队列例子:

        使用对象运算符重载函数的缺点:

                1.只能使用一个成员变量的比较,灵活性太差

class Data
{
    public:
        Data(int val1 = 10,int val2 = 10) : ma(val1),mb(val2) {}
        bool operator>(const Data& data) const { return ma > data.ma; }
        bool operator<(const Data& data) const { return ma < data.ma; } 
        int ma;
        int mb;
};

int main()
{
    //优先级队列
    priority_queue<Data> queue;
    queue.push(Data(10,20));
    queue.push(Data(11,30));
    queue.push(Data(12,40));
    return 0;
}

        使用lambda表达式:

class Data
{
    public:
        Data(int val1 = 10,int val2 = 10) : ma(val1),mb(val2) {}
        int ma;
        int mb;
};

int main()
{
    //优先级队列
    using FUNC = function<bool(Data&,Data&)>;
    //这样就可以在外部定义想要的比较方式了
    priority_queue<Data,vector<Data>,FUNC> queue([](Data &d1,Data &d2)->bool { return d1.ma > d2.ma;});
    queue.push(Data(10,20));
    queue.push(Data(11,30));
    queue.push(Data(12,40));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CID( ͡ _ ͡°)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值