C++学习笔记:函数绑定器(bind用法)

2023-11-03

绑定器bind总览

在这里插入图片描述
bind用于绑定可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针)和其参数。返回值为绑定成功后的函数对象。
在正式开始绑定器前,先进行一下知识补充。

补充1:函数对象

函数对象是可以以函数方式与()结合使用的任意对象,包括:(functor-仿函数)
1、函数名;
2、指向函数的指针;
3、重载了()操作符的类对象(即定义了函数operator()()的类)。在这里插入图片描述
转自:https://www.jianshu.com/p/1c986b510cff

补充2:一元函数以及一元断言/一元谓词

一元函数:函数的参数只有一个;
一元断言/一元谓词:函数的参数只有一个,并且返回类型是bool类型。

二元函数:函数的参数有两个;
二元断言/二元谓词:函数的参数两个,并且返回类型是bool类型。

//一元函数
int func(int b) { return b; }
//一元断言/一元谓词
bool func(int a) { return a>1; }

对于一元和二元断言,可以使用bind1st与bind2nd绑定。

template< class F, class T > std::binder1st<F> bind1st( const F& f, const T& x ); 
template< class F, class T > std::binder2nd<F> bind2nd( const F& f, const T& x );

#include <functional>
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>

using std::cout;
using std::endl;
using std::remove_if;
using std::copy;
using std::vector;
using std::ostream_iterator;

void test()
{
    
    vector<int> number = { 1, 5, 7, 9, 4, 6, 2, 8 };
    //copy函数将容器拷贝到输出迭代器中。
    copy(number.begin(), number.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    //remove_if仅仅将符合条件的元素放到容器头部排列,返回一个指向需删除元素的位置的迭代器。故还需要erase将无关元素删除。
    number.erase(remove_if(number.begin(), number.end(), bind2nd(std::greater<int>(), 4)), number.end());
    //auto it = remove_if(number.begin(), number.end(), bind2nd(std::greater<int>(), 4));
    //number.erase(it, number.end());

    copy(number.begin(), number.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
	
	/*
	1 5 7 9 4 6 2 8
	1 4 2
	*/
}


int main(int argc, char** argv)
{
    test();
    return 0;
}


补充3:占位符

头文件#include ,形式为std::placeholders::_n,占位符整体代表的是形参的位置,占位符中的数字代表的是实参的位置。

void func4(int x1, int x2, int x3, const int& x4, int x5)
{
    cout << "x1 = " << x1 << endl
         << "x2 = " << x2 << endl
         << "x3 = " << x3 << endl
         << "x4 = " << x4 << endl
         << "x5 = " << x5 << endl;
}

void test3()
{
    int number = 10;
    //bind采用的是值传递
    //ref = reference, 引用的包装器
    //cref = const reference, 引用的包装器
    auto f = bind(func4, 1, std::placeholders::_3, std::placeholders::_1, std::cref(number), number);
    number = 30;
    f(50, 40, 60, 70, 90, 80);//没有用到的参数就丢掉
	/*
	x1 = 1 直接传入的参数
	x2 = 60 实参列表第三位
	x3 = 50 实参列表第一位
	x4 = 30 number的引用,后续更改为30
	x5 = 10 传参时number的值
	*/
}

bind详解

bind函数的使用相比于bind1st以及bind2nd更加的具有通用性,因为后者只能绑定一个参数,而bind可以绑定任意个参数。

#include <iostream>
#include <functional>

using std::cout;
using std::endl;
using std::bind;
using std::function;

int add(int a, int b)
{
    cout << "int add(int, int)" << endl;
    return a + b;
}

class Example
{
public:
    int add(int a, int b)
    {
        cout << "int Example::add(int, int)" << endl;
        return a + b;
    }

    int data = 100;//C++11的初始化方式
};

void test()
{
    //function与bind结合使用,就可以实现多态
    
    //bind可以绑定add,可以改变函数add的形态(类型)
    //int(int, int)--->int()
    //函数也是有类型的(函数返回类型与函数的参数列表)
    /* auto f = bind(add, 1, 2); */
    function<int()> f = bind(add, 1, 2);
    cout << "f() = " << f() << endl;

    Example ex;
    //成员函数要取地址,需要明确写出取地址符号
    //int(int, int)--->int(int)
    /* auto f2 = bind(&Example::add, ex, 3, 5); */
    function<int()> f2 = bind(&Example::add, ex, 3, 5);
    cout << "f2() = " << f2() << endl;

    //占位符
    //int(int, int)--->int(int)
    /* auto f3 = bind(add, 4, std::placeholders::_1); */
    function<int(int)> f3 = bind(add, 4, std::placeholders::_1);
    cout << "f3(5) = " << f3(5) << endl;

    //bind可以绑定数据成员
    //int data = 100;
    f = bind(&Example::data, &ex);
    cout << "f() = " << f() << endl;
	/*
	int add(int, int)
	f() = 3
	int Example::add(int, int)
	f2() = 8
	int add(int, int)
	f3(5) = 9
	f() = 100
	*/
}

int main(int argc, char** argv)
{
    test();
    return 0;
}

注册和执行回调函数(多态)

#include <math.h>
#include <iostream>
#include <functional>

using std::cout;
using std::endl;
using std::bind;
using std::function;

class Rectangle
{
public:
    Rectangle(double length = 0, double width = 0)
        : _length(length)
        , _width(width)
    {
        cout << "Rectangle(double  = 0, double = 0)" << endl;
    }

    void display() const
    {
        cout << "Rectangle : ";
    }

    double area() const
    {
        return _length * _width;
    }

    ~Rectangle()
    {
        cout << "~Rectangle()" << endl;
    }
private:
    double _length;
    double _width;

};
class Circle
{
public:
    Circle(double radius = 0)
        : _radius(radius)
    {
        cout << "Ciecle(double)" << endl;
    }

    void print() const
    {
        cout << "Circle: print()" << endl;
    }

    double printArea() const
    {
        return _radius * _radius * 3.1415;
    }

    ~Circle()
    {
        cout << "~Circle()" << endl;
    }
private:
    double _radius;

};
class Triangle
{
public:
    Triangle(double a = 0, double b = 0, double c = 0)
        : _a(a)
        , _b(b)
        , _c(c)
    {
        cout << "Triangle(double  = 0, double = 0, double = 0)" << endl;
    }

    void show() const
    {
        cout << "Triangle: ";
    }

    double showArea() const
    {
        double tmp = (_a + _b + _c) / 2;

        return sqrt(tmp * (tmp - _a) * (tmp - _b) * (tmp - _c));
    }

    ~Triangle()
    {
        cout << "~Triangle()" << endl;
    }
private:
    double _a;
    double _b;
    double _c;
};

class Figure
{
public:
    //重定义
     /* typedef function<void()>  DisplayCallback;//C */
    using DisplayCallback = function<void()>;//C++11
    using AreaCallback = function<double()>;
    DisplayCallback _displayCallback;
    AreaCallback _areaCallback;

    //回调函数的注册
    void setDisplayCallback(const DisplayCallback& call)
    {
        _displayCallback = call;
    }

    void setAreaCallback(AreaCallback&& call)
    {
        _areaCallback = std::move(call);
    }

    //回调函数的执行
    void handleDisplayCallback() const
    {
        if (_displayCallback)
        {
            _displayCallback();
        }
    }

    double handleAreaCallback() const
    {
        if (_areaCallback)
        {
            return _areaCallback();
        }
        else
        {
            return 0;
        }
    }
};

void func(const Figure& fig)
{
    fig.handleDisplayCallback();
    cout << "area : " << fig.handleAreaCallback() << endl;
}

int main(void)
{
    Circle circle(10);

    cout << endl;
    Figure fig;
    //通过Figure类对象对不同的类进行托管,Figure类类似多继承中的基类。 
    //bind绑定类内的成员和this指针后,返回绑定成功的类匿名对象,让函数指针_displayCallback指向其匿名对象。
    //之后即可通过函数指针来执行类内的成员函数,达到实现多态的目的。
    fig.setDisplayCallback(bind(&Circle::print, &circle));
    fig.setAreaCallback(bind(&Circle::printArea, &circle));
    func(fig);
    return 0;
}

/*
结果
Ciecle(double)

Circle: print()
area : 314.15
~Circle()
*/

绑定器与继承实现多态的对比

对象关系:
关系为依赖,耦合度低。
关系为继承,耦合度更高。
代码量:
绑定器需要定义一个绑定类来绑定和执行回调函数,且内部需要定义所有可能用到的函数指针类型。每次切换对象,需要重新绑定所有函数,代码过于冗余。
继承仅需要在派生类内重写虚函数,调用时通过基类指针不断变化指向实现多态,个人感觉代码更为简洁。

具体使用哪种方式根据业务需求而定。

成员函数绑定器mem_fn

成员函数是一个受限制的函数,mem_fn可让其不用this指针,直接绑定,功能形同普通函数。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using std::cout;
using std::endl;
using std::vector;
using std::for_each;

class Number
{
public:
   Number(size_t data = 0) : _data(data){}

   void print() const
   {
       cout << _data << "  ";
   }

   //判断是不是偶数
   bool isEven() const
   {
       return (0 == _data % 2);
   }

   //质数
   bool isPrimer() const
   {
       if (1 == _data)
       {
           return false;
       }

       for (size_t idx = 2; idx != _data / 2; ++idx)
       {
           if (0 == _data % idx)
           {
               return false;
           }
       }

       return true;
   }
private:
   size_t _data;
};

void test()
{
   vector<Number> vec;
   for (size_t idx = 1; idx != 30; ++idx)
   {
       vec.push_back(Number(idx));
   }

   //成员函数是一个受限制的函数
   for_each(vec.begin(), vec.end(), std::bind(&Number::print, std::placeholders::_1));
   /* for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print)); */
   /* for_each(vec.begin(), vec.end(), &Number::print); error*/
   cout << endl;

   vec.erase(remove_if(vec.begin(), vec.end(), std::mem_fn(&Number::isEven)), vec.end());
   for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print));
   cout << endl;

   vec.erase(remove_if(vec.begin(), vec.end(), std::mem_fn(&Number::isPrimer)), vec.end());
   for_each(vec.begin(), vec.end(), std::mem_fn(&Number::print));
   cout << endl;
}

int main(int argc, char** argv)
{
   test();
   return 0;
}

/*
1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29
1  3  5  7  9  11  13  15  17  19  21  23  25  27  29
1  3  9  15  21  25  27
*/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++学习笔记:函数绑定器(bind用法) 的相关文章

随机推荐

  • gin框架23--绑定 HTML 复选框

    gin框架23 绑定 HTML 复选框 介绍 案例 说明 介绍 本文通过一个简单的案例 将结构体和html中的 form 数据绑定在一起 案例 源码 main go package main import github com gin go
  • 裸设备和Oracle问答20例

    1 什么叫做裸设备 裸设备 也叫裸分区 原始分区 是一种没有经过格式化 不被Unix通过文件系统来读取的特殊字符设备 它由应用程序负责对它进行读写操作 不经过文件系统的缓冲 2 如何辨别裸设备 在Unix的 dev 目录下 有许多文件 其中
  • 【C++设计模式】开放-封闭原则

    2023年8月27日 周日下午 我觉得我的这篇博客还是写得很不错的 哈哈哈 目录 概述 举例说明 用开放 封闭原则重构 概述 开放 封闭原则 Open Closed Principle OCP 是面向对象设计中的一个重要原则 也是许多设计模
  • shineblink CoreTFT串口屏开发

    TFT液晶触摸屏 一 开发基础准备工作 二 本章节实现功能介绍 三 接线图 四 开发板端完整代码 五 液晶屏页面的开发 六 代码运行结果 Shineblink Core 可支持3 5寸 4寸 7寸的TFT串口彩色液晶屏 本篇章主要演示了TJ
  • Windows 常用快捷键

    常用快捷键 Ctrl C 复制选定项 Ctrl X 剪切选定项 Ctrl V 粘贴选定项 Ctrl Z 撤消操作 Ctrl Y 重做操作 Ctrl S 保存 Ctrl A 选择文档或窗口中的所有项目 Alt Tab 在打开的应用之间切换 W
  • Qt Utils : To-Do

    Qt Creator自带的todo插件工具 真心舒爽 特别是对于我这种记不住三天前自己写的shit mountain的 渣渣CXY来讲 边撸代码边注释 快速查阅Task 非常重要 1 上效果图 2 工具使用 1 勾选使用插件 重启Qt Cr
  • 7-32 统计MOOC证书

    本题要求编写程序 输入N个学生的MOOC成绩 统计优秀 合格证书的数量 以及没有获得证书的数量 学生修读程序设计MOOC 85分及以上获得优秀证书 不到85分但是60分及以上获得合格证书 不到60分则没有证书 输入格式 输入在第一行中给出非
  • 如何安装新的PHP扩展模块

    一 phpize方式 该方法用于安装php源码ext目录中没有的扩展 1 下载源码 2 解压并进入扩展目录 3 执行phpize 4 执行 configure 5 make make install 6 在php ini 文件中添加 ext
  • JAVA识别复杂验证码+图像处理

    先对验证码进行简单的处理噪点和纯色 例未曾处理的图片 public static void main String args throws Exception 源文件 String picName C Users syxy101 Deskt
  • 您无权输入许可证密钥,请请使用系统管理员账户重试

    转载于 https www cnblogs com java2016 p 5448093 html
  • Android 解决Retrofit请求数据,数据过多,返回json数据乱码问题

    同一个POST请求返回的数据 有时候乱码 有时候不乱码 最后发现 数据量过多就会出现乱码 直接使用原生HttpUrlConnection请求不会产出乱码 我猜测就是框架配置的问题 然后在网上寻找解决方面发现一个帖子类似我这种问题 地址 问题
  • 小米9 MIUI12.5 红米 K40s MIUI13.0.10 安装谷歌框架

    前言 由于更换了小米9手机后 我想重新安装谷歌框架的 发现这谷歌框架安装不上了 下载第三方安装框架工具也是不行 然后看到一篇文章说小米9手机自带有个GMS服务框架 然后我试了一下还真的可以 所以分享一下 如果文章没了说明我已经进去了 设备
  • 【OJ刷题

    欢迎关注微信公众号 Python小灶 和我一起每天学习Python新知识 还可添加博主Vx yf03064131 方便一起交流学习 或者B站搜索 有只小新 文章目录 多数组合并 数组排序 最长的指定瑕疵度的元音字串 20210909 总共三
  • 2016的诗和远方

    当生活被眼前的苟且蒙住了双眼 其实你所希冀的远方 就在那噼啪敲击的键盘里 渗入脑海实现想法的一行行代码中 眼看一年就要结束了 坐在公司的办公区里 窗外霓虹灯五光十色 办公楼鳞次栉比 恍惚想起了这一年充满波折又熠熠生辉的日子 却又是欲买桂花同
  • 新建文件夹命令 linux,linux创建文件夹命令是什么

    linux创建文件夹命令是mkdir 语法为 mkdir 选项 参数 其中Z为设置安全上下文 当使用SELinux时有效 version显示版本信息 本文操作环境 linux2 6 32系统 DELL G3电脑 Linux中 mkdir 创
  • CEVA RivieraWaves™ Wi-Fi IP

    https www ceva dsp com product rivierawaves wi fi platforms OVERVIEW The RivieraWaves Wi Fi IP family is a comprehensive
  • 提交本地项目到GitHub

    文章目录 1 下载git 1 1 通过homebrew安装Git 1 2 通过Xcode安装 2 创建ssh key 配置git 3 提交本地项目到GitHub 说明 该博文参考这篇文章和这段视频 1 下载git 1 1 通过homebre
  • 全面了解Maven依赖范围

    Maven依赖范围 翻译原文详见 Maven Dependency Scopes 1 总览 Maven 是 Java 生态系统中最流行的构建工具之一 其核心特性之一是依赖管理 Maven is one of the most popular
  • vue应用的简单例子、$.get()方法及页面获取url里面的参数

    vue应用的简单例子 get 方法及页面获取url里面的参数 页面 要先引入vue的js div 123 hospital name div
  • C++学习笔记:函数绑定器(bind用法)

    绑定器bind总览 bind用于绑定可调用 Callable 对象 函数对象 指向函数指针 到函数引用 指向成员函数指针或指向数据成员指针 和其参数 返回值为绑定成功后的函数对象 在正式开始绑定器前 先进行一下知识补充 补充1 函数对象 函