2023/4/17笔记

bind函数

std::bind函数定义在头文件functional中,是一个函数模板,是函数适配器

image-20230417194329368
f	-	Callable object (function object, pointer to function, reference to function, pointer to member function, or pointer to data member) that will be bound to some arguments(参数).

Callable object 调用对象,可以是函数对象(function object)、函数指针(pointer to function)、函数引用(reference to function)、成员函数指针(pointer to member function)、数据成员指针(pointer to data member).

args	-	list of arguments to bind, with the unbound arguments replaced by the placeholders _1, _2, _3... of namespace std::placeholders

未绑定的参数将会被占位符 _1 、 _2、 _3所代替。

Return value
A function object of unspecified type T, for which std::is_bind_expression<T>::value == true. It has the following members:

一个未指定类型 T 的函数对象,bind函数返回类型不确定所以用auto

函数对象就是函数名

代码测试:

#include <iostream>
#include <functional>

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

int add(int x, int y){
    cout << "int add(int,int)" << " = ";
    return x + y;
}

int func(int x,int y, int z){
    cout << "int func(int,int,int)" << " = ";
    return x + y + z;
}

class Example
{
public:
    int add(int x, int y)
    {
        cout << "int Example::add(int, int)" <<  " = ";
        return x + y;
    }
    static int print(int x,int y)
    {
        cout << "int Example::print(int, int)" <<  " = ";
        return x + y;
    }
    int data = 1;   //C++11,在声明数据成员的时候可以直接初始化
};

//1、bind函数可绑定多个参数
void test(){
    
    auto f = bind(add, 1, 2); //调用add函数,绑定了两个参数
    cout << "f() = " << f() << endl << endl;

    //func函数的类型(标签):int(int,int,int)
    auto f2 = bind(&func,1,2,3); //调用func函数,绑定了三个参数,
    cout << "f2() = " << f2() << endl << endl;
}

//2、bind函数可以传入不同的调用对象
void test2(){
    //(1)传入函数对象
    auto f = bind(add, 1, 2); //调用add函数,绑定了两个参数
    cout << "f() = " << f() << endl << endl;

    //(2)传入函数指针,类型(标签):int(int,int,int)
    auto f2 = bind(&func,1,2,3); 
    cout << "f2() = " << f2() << endl << endl;

    //(3)传入成员函数指针
    Example ex; //成员函数add的类型:int(Example*, int ,int)
    auto f3 = bind(&Example::add, &ex, 10, 30); // 成员函数隐藏this指针
    cout << "f3() = " << f3() << endl << endl;

    //静态成员函数print的类型:int(int, int)
    auto f4 = bind(&Example::print, 10, 30); //静态成员函数没有this指针
    cout << "f4() = " << f4() << endl << endl;

    //(4)传入数据成员指针
    Example ex2;	//将数据成员假看作为成员函数,是无参
    auto f5 = bind(&Example::data,&ex2); //data与ex2绑定
    cout << "f5() = Example::data = " << f5() << endl << endl;

}

//3、bind函数的占位符placeholders使用
void func_ph(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;
    using namespace std::placeholders; //占位符
    auto f = bind(func_ph, 1, _4, _2, std::cref(number),number);
    number = 777;
    f(100, 200, 300, 400, 500, 600, 700); //多余的参数直接丢掉
    //std::ref = reference 引用
    //std::cref = const reference 常量引用
    //(1) 占位符本身代表的是形参的位置、占位符中的数字代表的是实参的位置
    //(2) 实际传参的个数一定要大于等于占位符中传递最大数字
    //(3) bind绑定的函数默认采用的是值传递,如果想使用引用传递,可以使用引用的包装器
}

//bind绑定的函数,进行传参时默认采用的是值传递
void test4(){
    Example ex; 
    auto f = bind(&Example::add, ex, 10, 30); //此处可以直接使用值传递传递ex对象
    cout << "f() = " << f() << endl;
}

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

image-20230417204335079image-20230417204250351image-20230417204211226

从左到右分别是test( )、test2( )、test3( ) 的执行结果。

注意事项:

1、在C语言中,函数名是函数的入口地址,在取地址与不取地址是一样;在C++中,普通函数还是具备该特点,但C++中成员函数的地址,必须严格写出来
2、变量有类型 对象也有类型,函数也是有类型:包括函数的参数列表与函数的返回类型
3、函数的类型也可以称为函数的标签,全局add函数的类型:int(int, int)
4、对于 f 而言,已经通过bind将add函数的参数都设置了值,f 的类型就是int(),所以bind是可以改变函数形态的(重要)

function类

image-20230417203442852

类模板std::function是一个通用的多态函数包装器std::function的实例可以存储、复制和调用任何可调用目标--函数、lambda表达式、绑定表达式或其他函数对象,以及成员函数的指针和数据成员的指针。

存储的可调用对象被称为std::function的目标。如果一个 std::函数不包含目标,它就被称为空函数。调用一个空的std::function的目标会导致抛出std::bad_function_call异常。

std::function 满足 CopyConstructible 和 CopyAssignable 的要求。

查看一下function类的构造函数

image-20230417205350430 image-20230417205604953

既然这样,std::function的实例也可以接收bind函数返回的函数对象,并对其可以进行操作。

void test4()
{
    //func函数的类型(标签):int(int, int, int)
    //f的函数类型是int()
    function<int()> f = bind(&func, 1, 2, 3); 
    cout <<"f() = " << f() << endl << endl;

    //成员函数add的类型:int(Example *, int, int)
    Example ex;	
    //f2的函数类型是int()
    function<int()> f2 = bind(&Example::add, &ex, 10, 30);
    cout << "f2() = " << f2() << endl << endl;

    //add函数类型:int(int, int)
    //f3的函数类型是int(int),因为有一个占位符存在
    function<int(int)> f3 = bind(add, 10, std::placeholders::_1);
    cout << "f3(90) = " << f3(90) << endl << endl; //占位符传入90

   	//将数据成员假看作为成员函数,是无参
    Example ex2;
    function<int()> f4 = bind(&Example::data, &ex2);
    cout << "f4() === " << f4() << endl;
}
image-20230417213226681

注意事项:

1、对于f而言,已经通过bind将add函数的参数都设置了值,f的类型就是int()

2、而使用占位符后,就有了一个参数,f的类型就是int(int)

3、f 的函数形态int() ,function称为函数的容器

回调函数

说到回调函数自然离不开函数指针的使用,先讲解一下函数指针的使用。

函数指针

声明一个函数指针: void (*foo)();

  • () 说明 foo 是一个函数,无参数
  • (*foo) 说明 foo 是个指针,即函数指针变量的名字
  • void 说明 foo 的返回类型是void

例如:

int (*f1)(double);	//传入double,返回int
void (*f2)(char*);	//传入char指针,没有返回值
double* (*f3)(int,int);	//传递两个整数,返回double指针

函数指针的特征:用小括号将 ”指针“ 的星号与函数名绑定在一起。

使用函数指针

int square(int num){ // 定义一个int(int)的函数
    return num * num;
}
void test(){
    int (*fptr1)(int); //声明一个int(int)的函数指针

    int n = 5;
    fptr1 = square;	//将square函数地址赋予函数指针fptr1

    printf("%d squared is %d\n",n, fptr1(n)); //向函数对象内传参,调用执行函数
    //如果是无参,则是fptr1()
}

这就看出来函数指针的声明的过程就像是声明一个变量,最后为这个变量赋值。

类型定义

类比为变量的数据类型起别名,为函数指针声明一个类型定义会比较方便。

通常,类型定义的名字是声明的最后一个元素。

int square(int num){ // 定义一个int(int)的函数
    return num * num;
}
void test(){
    //声明一个函数指针的类型定义
    typedef int (*funcptr)(int); //所以函数指针的类型定义为funcptr,funcptr就是那个元素

    int n = 5;
    funcptr fptr2 = square;	//funcptr初始化一个函数对象,并初始化为square函数地址

    printf("%d squared is %d\n",n, fptr2(n)); //向函数对象内传参,调用执行函数
    //如果是无参,则是fptr2()
}

传递函数指针

返回函数指针

回调函数

注意:C语言中的函数都是普通函数,函数名就是函数的入口地址。而在C++中,像成员函数必须用&传递函数地址

void demo()
{
    func2();
   
    typedef int (*pFunc)(); //声明一个函数指针的类型定义
    //延迟调用的思想
    pFunc f = &func2;//初始化一个函数指针对象,这一过程就叫做注册回调函数
    cout << "f() = " << f() << endl;//执行回调函数
}

多态的实现方式

面向对象

通过面向对象的思想实现多态

//抽象类,作为接口使用
class Figure
{
public:
    //纯虚函数
    virtual void display() const = 0;
    virtual double area() const = 0;
};
//省略Rectangle、Circle、Triangle类的实现过程,都是继承的Figure类
void func(const Figure &fig)
{
    fig.display();
    cout << "的面积 : " << fig.area() << endl;
}

int main(int argc, char **argv)
{
    Rectangle rectangle(10, 20);
    Circle circle(10);
    Triangle triangle(3, 4, 5);

    func(rectangle);
    func(circle);
    func(triangle);

    return 0;
}
image-20230417222121232

面向过程

通过面向过程的思想实现多态

关于using的小知识点:

typedef function<void()> DisplayCallback;	//C++98 

using  DisplayCallback = function<void()>;	//C++11, 等价于c++98的类型定义别名
using  AreaCallback = function<double()>;	//C++11 
//抽象类,作为接口使用
class Figure
{
public:
    //virtual void display() const = 0;
    //virtual double area() const = 0;

    using DisplayCallback = function<void()>;
    using AreaCallback = function<double()>;

    //定义数据成员
    DisplayCallback _displayCallback; //定义的是一个函数对象
    AreaCallback _areaCallback; //定义的是一个函数对象

    //注册回调函数
    void setDisplayCallback(DisplayCallback &&cb){
        _displayCallback = std::move(cb);
    }
    void setAreaCallback(AreaCallback &&cb){
        _areaCallback = std::move(cb);
    }

    //执行回调函数
    void handleDisplayCallback() const {
        if(_displayCallback){ //如果函数对象不为空
            _displayCallback(); //调用执行函数
        }
    }
    double handleAreaCallback() const {
        if(_areaCallback){  //如果函数对象不为空
            return _areaCallback();  //调用执行函数,并返回函数执行结果
        }
        else{
            return 0.0;
        }
    }
};
//省略Rectangle、Circle、Triangle类的实现过程,都没有继承Figure类

void func(const Figure &fig) //const对象只能调用const成员函数
{
    fig.handleDisplayCallback();
    cout << "的面积 : " << fig.handleAreaCallback() << endl;
}

int main(int argc, char **argv)
{
    Rectangle rectangle(10, 20);
    Circle circle(10);
    Triangle triangle(3, 4, 5);

    //1、初始化一个对象
    Figure fig;

    //2、回调函数的注册
    //DisplayCallback cb = bind(&Rectangle::display,&rectangle);
    fig.setDisplayCallback(bind(&Rectangle::display,&rectangle)); //调用打印函数
    fig.setAreaCallback(bind(&Rectangle::area,&rectangle)); //执行面积计算函数
    func(fig);

    fig.setDisplayCallback(bind(&Circle::display,&circle));
    fig.setAreaCallback(bind(&Circle::area,&circle));
    func(fig);

    fig.setDisplayCallback(bind(&Triangle::display,&triangle));
    fig.setAreaCallback(bind(&Triangle::area,&triangle));
    func(fig);
    

    return 0;
}


image-20230417230633526
posted @ 2023-04-17 23:15  雪国北风  阅读(40)  评论(0编辑  收藏  举报