当前位置: 首页 > news >正文

C++11 wrapper装饰器 bind+function

原文链接:C++11 wrapper装饰器 bind+function

前言

装饰器本身是为了更好的支持多态性,减小开发的复杂度和代码量.

c++11标准

bind函数

格式
//定义
template< class F, class... Args >
bind( F&& f, Args&&... args );//示例
int func(int a,int& b){return a+b;
}
int b=2;
auto func1=std::bind(func,a,std::ref(b));第一个参数就是函数指针,如果是普通成员函数的话,第二个参数为实例对象引用func1大致类型为:
(std::_Bind, std::reference_wrapper))(int, int&)>)不同的封装格式不同,例如使用占位符的格式会增加placeholders部分

按绑定函数类型分:

  1. 普通函数
  2. 普通成员函数
  3. 静态成员函数

按参数类型分:

  1. 普通参数
  2. 引用参数
  3. 占位符
特性
  1. const类型参数在bind里面基本上没啥意义,普通参数bind之后,对应参数的值也不会变化
  2. 占位符必须从_1开始递增使用
  3. 占位符不能替代引用参数, 引用参数能在函数内被修改,占位符不能实现这个特性
示例
#include<functional>
#include<iostream>using namespace std::placeholders;int func(int a,int& b){return a+b;
}class C{
public:C(){}static int scall(int a,int& b){return a+b;}int call(int a,int& b) {return a+b;}};int main(){int a=1;int b;//1.普通函数auto func1=std::bind(func,a,std::ref(b));b=2;std::cout<<"1 普通函数 sum(a,ref(b))="<<func1()<<"\n";//2. 普通成员函数C c;auto func2=std::bind(&C::call,&c,a,std::ref(b));b=3;std::cout<<"2 普通成员函数 C.c.sum(a,ref(b))="<<func2()<<"\n";//3. 静态成员函数auto func3=std::bind(&C::scall,a,std::ref(b));b=4;std::cout<<"3 静态成员函数 C.sum(a,ref(b))="<<func3()<<"\n";//4.使用占位符auto func4=std::bind(func,_1,std::ref(b)); //_1到_20,占位符必须对应参数位置b=5;std::cout<<"1 普通函数 sum(a,ref(b))="<<func4(-5)<<"\n";}

function对象

格式

function对象是一个能够表示重载operator()的类对象,可以理解为一个实现了指定函数功能的对象

// 普通函数
int func(int a,int& b){return a+b;
}
// 普通函数对象
std::function<int(int,int&)> fobj=func;
使用fobj(arg1,arg2) 调用//与bind搭配使用
std::function<int()> fobj_bind=std::bind(func,a,std::ref(b));
使用fobj() 无参调用,并且参数b是引用传值//函数替换 定义的函数可以通过bind再替换为其他函数
fobj_bind=std::bind(func,a,-a);
特性

function对象相比函数指针,主要的优势

  1. 简单直观 function定义更符合对象定义,而指针类型定义复杂
  2. 灵活 函数对象存放的函数可以替换
  3. 优化 函数对象编译时会自动内联调用 函数指针无法实现
  4. c++对function就行了优化, 使用function相比函数指针会更安全
示例
#include<functional>
#include<iostream>using namespace std::placeholders;int func(int a,int& b){return a+b;
}class C{
public:C(){}static int scall(int a,int& b){return a+b;}int call(int a,int& b) {return a+b;}};int main(){int a=1;int b=2;std::function<int(int,int&)> fobj=func;std::function<int()> fobj_bind=std::bind(func,a,std::ref(b));std::cout<<"调用普通函数的function对象:"<<fobj(a,b)<<"\n";b=3;std::cout<<"调用bind封装后函数的function对象:"<<fobj_bind()<<"\n";fobj_bind=std::bind(func,a,-a);std::cout<<"function对象替换内部函数:"<<fobj_bind()<<"\n";return 0;
}

为什么需要装饰器wrapper? : 更好的支持多态

c++函数指针类型

先了解C++的函数,一般函数定义类似为:
int(*sum)(int,int)

sum为函数指针变量,对应的类型就是int(*)(int,int)

两个相同类型的函数指针变量可以相互赋值.

#include<iostream>
int main(){int(*sum)(int,int);int(*tmp)(int,int)=[](int a,int b){return a+b;};std::cout<<"tmp(1,2)="<<tmp(1,2)<<"\n";sum=tmp;std::cout<<"sum(1,2)="<<sum(1,2)<<"\n";return 0;
}
//output:
// tmp(1,2)=3
// sum(1,2)=3 

但是类的成员函数有所不同,例如类A的普通成员函数sum类型为: int(A:😗)(int,int) 其中包含了A这个类.

如果想声明为普通的函数指针形式,那么需要加上static,表示静态成员函数.

没有装饰器,函数指针下实现基类调用派生类函数

现在假设一个场景,抽象基类Base有一个回调函数指针,Base定义一个interface函数.

要求派生类的实例通过基类的函数调用自身定义的函数.

#include<functional>
#include<iostream>class Base{
public:virtual void interface()=0;
protected:void(*callback)()=nullptr;
};class DerivedB:Base{
public:DerivedB(){callback=&DerivedB::func1;}void interface() override{callback();}
private:static void func1(){std::cout<<"func1"<<"\n";}
};int main(){DerivedB db;db.interface();return 0;
}

可以看到,该例中,我们要实现静态的成员函数,然后赋值基类的函数指针,再重载基类的函数.

一套下来非常复杂,完全没有减少代码量

使用装饰器

使用function定义一个回调函数对象,然后在派生类中赋值bind后的函数,就能够直接使用基类的方法调用到派生类的函数.

#include<functional>
#include<iostream>class Base{
public:void interface(){callback();};
protected:std::function<void()> callback;
};class DerivedB:public Base{
public:DerivedB(){callback=std::bind(&DerivedB::func1,this);}
private:void func1(){std::cout<<"func1"<<"\n";}
};int main(){DerivedB db;db.interface();return 0;
}

wrapper的意义

场景

假设一个网络服务器应用,在服务响应架构设计时,我们分为两个模块: 回调实现模块 和 回调函数调用模块.

使用函数指针定义回调函数时,不同响应行为,回调函数的参数或者返回类型不一样,此时无法实现一个通用的函数完成该模块.
只能定义一个接口,在每个响应行为拍摄类中重载这个接口,完成回调功能.

但是使用function定义回调函数对象,将该对象定义为一个无参的函数,具体的封装由派生类中实现.
我们在基类中就能完成回调函数调用模块.

在派生类中只需要把对应行为的回调函数和参数封装,传递给基类指针即可.

优点

  1. 能更简单的实现多态性
  2. 能提升应用的模块化程度

http://www.mrgr.cn/news/52442.html

相关文章:

  • 强推!首个全面涵盖LiDAR里程计算法的综述(二):多激光与多传感器融合激光里程计综述
  • AcWing 278. 数字组合
  • 本地群晖NAS安装phpMyAdmin管理MySQ数据库实战指南
  • 德国注册公司的主要流程和要求
  • 递归知识简记
  • 解读 Java 经典巨著《Effective Java》90条编程法则,第3条:用私有构造器或者枚举类型强化Singleton属性
  • 实验五 JSP编程
  • Spring 注解揭秘:@Autowired 和 @Resource 的用法详解与避坑建议
  • mysql高级sql语句 二
  • 【力扣热题100】3194. 最小元素和最大元素的最小平均值【Java】
  • 文心一言 VS 讯飞星火 VS chatgpt (371)-- 算法导论24.4 3题
  • 6CXX:UICC告诉终端数据长度
  • 【Python爬虫】看电影还在用VIP?一个python代码让你实现电影自由!附源码
  • 跟李沐学AI—pytorch版本锚框代码解析
  • 解读 Java 经典巨著《Effective Java》90条编程法则,第5条:优先考虑依赖注入来引用资源
  • esxi配置磁盘直通虚拟机
  • [每日一练]利用apply函数和lambda函数实现数据的聚合
  • C++ IO多路复用 epoll模型
  • Qml-Item的构造和显示顺序
  • RISC-V笔记——显式同步