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

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.kler.cn/news/353676.html

相关文章:

  • 【服务器知识】Tomcat简单入门
  • 10月17日,每日信息差
  • Leetcode 最小栈
  • 小白投资理财 - 中国股票代号
  • NVIDIA Bluefield DPU上的启动流程4个阶段分别是什么?作用是什么?
  • 机器学习——主要分类
  • 2024软件测试面试大全(答案+文档)
  • Springboot 整合 Java DL4J 实现安防监控系统
  • 前端布局,y轴超出滚动、x轴超出展示方案
  • 全金属的两足机器人钢铁侠开发
  • [山河2024] week2
  • 基于机器学习的心脏病风险评估预测系统
  • JavaScript获取array中相同key的平均值
  • 基于SSM电子资源管理系统的设计
  • 《计算机视觉》—— 疲劳检测
  • Android Gralde 新版aar依赖问题解决
  • 滚雪球学Redis[4.2讲]:Redis Sentinel 深度解析:工作原理、配置与高可用架构下的故障转移
  • 如何打开荣耀手机的调试模式?
  • Vue 3 的不同版本总结
  • 自动化运维的研究与应用