C++绑定器和函数对象

简介: band1st和band2nd本身还是一个函数对象。

简介


C++ STL中的绑定器


  • bind1st:operator()的第一个形参变量绑定一个确定的值。
  • bind2nd:operator()的第二个形参变量绑定一个确定的值。


C++ Boost库


C++的Boost库中引入了bind绑定器和function函数对象机制。


lambda表达式


lambda表达式由底层依赖函数对象的机制实现。


C++ STL中的绑定器


初识band1st和band2nd


band1st和band2nd本身还是一个函数对象。


将STL中内置的二元函数对象转化为一元函数对象使用。


#include<iostream>
#include<functional>
#include<vector>
#include<algorithm>
#include<time.h>
using namespace std;
template<typename Container>
void showContainer(Container &con){
    //由于编译的原因,所以需要加上typename,防止无法识别Container类型
    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++)
    {
        //产生1~100之间的随机数
        vec.push_back(rand() % 100 + 1);
    }
    showContainer(vec);
    sort(vec.begin(), vec.end());
    showContainer(vec);
    // greater二元函数对象,用于从大到小排序
    sort(vec.begin(), vec.end(), greater<int>());
    showContainer(vec);
    // bind1st和bind2nd可以将二元函数对象转化为一元函数对象,也就是说传入的默认值对应二元函数对象的形参
    // 寻找第一个小于70的索引,原序列按照从大到小排序 return 70>*it 
    // auto it = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70));
    // 寻找第一个小于70的索引,原序列按照从大到小排序 return *it<70
    auto it = find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 70));
    if(it!=vec.end())
    {
        vec.insert(it, 70);
    }
    showContainer(vec);
    return 0;
}


自己实现band1st和find_if


#include<iostream>
#include<functional>
#include<vector>
#include<algorithm>
#include<time.h>
using namespace std;
template<typename Container>
void showContainer(Container &con){
    //由于编译的原因,所以需要加上typename,防止无法识别Container类型
    typename Container::iterator it = con.begin();
    for (; it != con.end();it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}
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;
};
template <typename Compare, typename T>
_mybind1st<Compare, T> mybind1st(Compare comp, const T &val)
{
    return _mybind1st<Compare, T>(comp, val);
}
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first,Iterator last,Compare comp){
    for (; first != last;first++){
        //comp.operator()(*first)
        if(comp(*first)){
            return first;
        }
    }
    return last;
}
int main(){
    vector<int> vec;
    srand(time(nullptr));
    for (int i = 0; i < 20;i++)
    {
        //产生1~100之间的随机数
        vec.push_back(rand() % 100 + 1);
    }
    showContainer(vec);
    sort(vec.begin(), vec.end());
    showContainer(vec);
    // greater二元函数对象,用于从大到小排序
    sort(vec.begin(), vec.end(), greater<int>());
    showContainer(vec);
    auto it = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 70));
    // auto it = my_find_if(vec.begin(), vec.end(), bind2nd(less<int>(), 70));
    if(it!=vec.end())
    {
        vec.insert(it, 70);
    }
    showContainer(vec);
    return 0;
}


bind函数


C++11中的bind绑定器的返回结果还是一个函数对象。


初识bind函数


#include<iostream>
#include<string>
#include<functional>
using namespace std;
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是函数模板,可以自动推演模板类型参数,第一个参数是函数的指针
    bind(hello, "hello,world")(); 
    cout << bind(sum, 10, 20)() << endl;
    //bind使用函数对象的时候,必须要利用函数对象才能使用
    cout << bind(&Test::sum, Test(), 10, 20)()<<endl;
    // placeholders是占位符
    bind(hello, placeholders::_1)("hello,bind2!");
    bind(sum, placeholders::_1, placeholders::_2)(1, 3);
    function<void(string)> func1 = bind(hello, placeholders::_1);
    func1("hello,china!");
    func1("hello,shanghai!");
    return 0;
}


bind和function结合多线程使用


#include<iostream>
#include<string>
#include<thread>
#include<vector>
#include<functional>
using namespace std;
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::runThread,this,i)));
            }
            for (int i = 0; i < size; i++)
            {
                _handler.push_back(_pool[i] -> start());
            }
            for (int i = 0; i < size;i++)
            {
                _handler[i].join();
            }
        }
    private:
        vector<Thread*> _pool;
        vector<thread> _handler;
        void runThread(int id)
        {
            cout << "call run Thread id :" << id << endl;
        }
};
int main()
{
        ThreadPool pool;
        pool.startPool(10);
        return 0;
}


代码打印结果,由于是多线程,所以打印结果是乱序的。


call run Thread id :call run Thread id :call run Thread id :2call run Thread id :5
call run Thread id :61
call run Thread id :9
call run Thread id :4
call run Thread id :8
0
call run Thread id :
call run Thread id :37


C++中的lambda表达式


函数对象的主要缺点:需要先定义函数,并初始化函数对象才能被使用。


C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。


Lambda 的语法形式如下:


[captures](params) -> return_type { body };


Lambda 主要分为五个部分,对应为:


[捕获列表] (函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}


上式lambda表达式中->可以省略。


捕获列表是 Lambda 表达式最有意思的地方,这里重点介绍下捕获列表。


捕获列表,用来说明外部变量的访问方式,外部变量访问方式说明符可以是 = 或 & ,表示函数体中用到的、定义在外面的变量在函数体中是否允许被改变。= 表示值传递,不允许改变。& 表示引用传递,允许改变。


包括下面几种形式:


1、[ ] 表示不捕获任何变量

2、[=] 表示按值传递的方法捕获父作用域的所有变量

3、[&] 表示按引用传递的方法捕获父作用域的所有变量

4、[=, &a] 表示按值传递的方法捕获父作用域的所有变量,但按引用传递的方法捕获变量a

5、[&, a] 表示按引用传递的方法捕获父作用域的所有变量,但按值传递的方法捕获变量a

6、[this] 捕获外部的指针


更多lambda表达式的介绍可以参考C++ 之 Lambda 表达式


mutable在lambda表达式返回类型前加上,可以在以值传递的形式上修改以值传递的变量值。


C++中的函数对象


function的作用在于:绑定器、函数对象、lambda表达式,它们只能使用在一条语句中。


初识function


#include<iostream>
#include<string>
#include<algorithm>
#include<functional>
#include<ctime>
using namespace std;
void hello1()
{
    cout << "hello,world!" << 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;
    }
};
int main(){
    /* 
    function函数的使用方法,function<typename(typename1,...)> xxx=yyy;
    typename表示函数的返回类型,typename_1表示寒暑的参数类型,xxx表示function重命名的函数,yyy表示原来的函数
    */
    //  调用函数hello1(),function使用的是函数类型
    function<void()> func1 = hello1;
    // 调用函数hello1(),function使用的是函数指针类型
    // function<void(*)> func1 = hello1;
    // 本质上和hello1()没有区别,调用了func1对象()重载函数->hello1.operator()
    func1();
    function<void(string)> func2 = hello2;
    func2("hello2!");
    function<int(int, int)> func3 = sum;
    cout << func3(10, 20) << endl;
    function<int(int, int)> func4 = [](int a, int b)->int{ return a + b; };
    cout << func4(10, 20);
    function<void(Test*, string)> func5 = &Test::hello;
    func5(&Test(), "call Test::hello!");
    return 0;
}


自定义实现function


本质上是实现一个类,利用构造函数和()操作符重载的功能是实现函数绑定。


#include<iostream>
#include<functional>
#include<string>
using namespace std;
void hello(string str)
{
    cout << str << endl;
}
int sum(int a,int b)
{
    return a + b;
}
template<typename Fty>
class myfunction{
};
/*
template<typename R,typename A1>
class myfunction<R(A1)>
{
    public:
        //函数返回的指针参数类型
        using PFUNC = R (*)(A1);
        myfunction(PFUNC pfunc) : _pfunc(pfunc){};
        R operator()(A1 arg){
            return _pfunc(arg);
        }
    private:
    PFUNC _pfunc;
};
template <typename R, typename A1,typename A2>
class myfunction<R(A1,A2)>
{
public:
    // 函数返回的指针参数类型
    using PFUNC = R (*)(A1,A2);
    myfunction(PFUNC pfunc) : _pfunc(pfunc){};
    R operator()(A1 arg1,A2 arg2)
    {
            return _pfunc(arg1,arg2);
    }
private:
    PFUNC _pfunc;
};
*/
// ...表示可变参数类型个数,和上面两个类的功能是一样的
template <typename R, typename... A>
class myfunction<R(A...)>
{
public:
    // 函数返回类型的指针,函数的参数类型列表
    using PFUNC = R (*)(A...);
    myfunction(PFUNC pfunc) : _pfunc(pfunc){};
    R operator()(A... arg)
    {
            return _pfunc(arg...);
    }
private:
    PFUNC _pfunc;
};
int main(){
    //就是构造函数重命名了hello函数
    myfunction<void(string)> func1(hello);
    func1("hello1");
    myfunction<int(int, int)> func2(sum);
    cout << func2(2, 3) << endl;
    return 0;
}
目录
相关文章
|
2月前
|
编译器 C++
C++之类与对象(完结撒花篇)(上)
C++之类与对象(完结撒花篇)(上)
40 0
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
83 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
80 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
87 4
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
31 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
32 4
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
79 6
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
37 0
C++ 多线程之线程管理函数
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)