7. cpp函数对象与绑定器

37 阅读4分钟

一、函数指针

作用在于声明函数时指定回调函数类型,函数指针是指向同返回值、参数列表的函数,与普通指针指向地址有些许区别

1. 函数指针类型

// 法一
typedef int(*Func_int_int)(int,int);
// 法二
using Func_int_int = int(*)(int,int);//容易忘记中间的*

2.函数类型

注意不存在函数变量,所以该类型无法定义变量,在特定场景下使用,作用仅在于声明函数指针或引用以及模板中传递类型参数

// 只是不要函数指针中间的*
using Func_int_int_class = int(int,int);

2. 函数指针变量

函数指针,使得函数可以保留在数组中(跳转表),也使得函数能在函数中传递并调用

  • 在形参中使用如下方法声明函数指针,实参可以使用函数名直接赋值,也可以使用&函数名,调用时前面加不加*都可以
// 法一
int (*funcptr)(int,int);
// 法二,使用函数指针类型,和普通变量一致
Func_int_int funcptr;

3. 成员函数指针

对于非静态成员函数,必须使用::*声明,取地址&::赋值,->*.*解引用调用
静态成员函数指针,和普通函数指针基本一致,只是在赋值时需要加上类的作用域&MyClass::myFunc return_type (Class_name::*pointer_name)(parameter_types);

class MyClass { 
public: 
    int func(int x) { 
        return x * 2; 
    } 
}; 
// 法一 
int (MyClass::*memFuncPtr)(int) = &MyClass::func;
// 法二
using MemFuncPtr = int (MyClass::*)(int); 
MemFuncPtr ptr = &MyClass::func;

MyClass obj;
MyClass *pObj = &obj;
// 调用方式 
(obj.*memFuncPtr)(); // 通过对象调用 
(pObj->*memFuncPtr)(); // 通过指针调用

不足

  • 函数指针变量必须要运行后才知道具体函数代码,无法在编译时内联

二、 函数对象

重载了operator()的类对象

1. 函数对象实例

  1. 创建方式
  • 手动定义该类后实例化(便于理解底层原理)
  • STL在functional中提供了大量函数类模板,如greater<T>,使用时需要将其实例化为函数对象,如greater<int>()
  • lambda创建
  • 函数绑定器
  1. 函数对象与函数指针

函数指针有普通和对象函数指针,普通函数名就是函数指针(类似于数组),而对象函数名需要取地址才是函数指针(类似变量)

  • 共同点:都是在后面加()后就能被调用
  • 不同点
    • 两者不可交叉赋值
    • 函数指针是指向代码区,要运行后才知道实际代码,所以无法内联

2. 函数对象类型

上面提过函数对象实例化后需要使用变量接受,变量又需要指定类型

  • 自定义函数对象类型
  • std::function<函数类型>:因为lambda创建的对象,并没有类型变量来接受,所以需要添加一个快速指定类型的方式。其本质上是函数对象类模板,内部存在函数指针指向传入的函数,实现需要使用类型推导、可变模板参数等 image.png

三、绑定器

  • 底层都是函数模板,返回的是函数对象,底层依然是调用传入的函数
  • 可以使用参数占位符,代表需要用户传递参数,function<void(string)> func1 = bind(print,placeholder::_1);
  1. bind1st
  2. bind2st
  3. bind

image.png

// 上面代码中返回的是自定义的函数对象_mybind1st

四、lambda

  1. 语法:[捕获外部变量](参数列表)->返回类型{函数体},如果没有返回值,可简写为[捕获外部变量](参数列表){函数体} image.png
  2. 本质就是对象函数,捕获列表中变量对应构造函数中的参数,如果存在变量,则每个变量会有对应的成员数据,参数列表则对用operator()中的参数,函数体中的所有变量都会替换为对象中对应的成员数据

注意捕获列表如果是值传递,则operator()会被优化为const,函数体不能出现捕获列表参数的更改,如果要更改,可以使用mutable,但是并不会对捕获的变量产生任何修改

int x = 10; 
auto lambda = [x](int y) { return x + y; };

//==>

class __AnonymousLambda { 
    const int x; // 值捕获的变量是const成员 
public: 
    __AnonymousLambda(int x_) : x(x_) {} // 构造函数初始化捕获变量 
    int operator()(int y) const { return x + y; } // 默认const 
};
  1. 无捕获lambda可以转换成函数指针,底层借助重载类型转换和静态方法实现
auto lambda = [](int a, int b) { return a + b; };
int (*funcPtr)(int, int) = lambda; // 隐式调用 operator int (*)(int, int)()

// 编译器实际生成以下内容:
// 1. 生成一个匿名类
class __Lambda_123 {
public:
    int operator()(int a, int b) const { 
        return a + b; 
    }
    // 关键:隐式转换为函数指针的运算符
    operator int (*)(int, int)() const { 
        return static_cast<int (*)(int, int)>(&__Lambda_123::__invoke);
    }
private:
    // 2. 生成一个静态成员函数(实际执行 Lambda 体的函数)
    static int __invoke(int a, int b) {
        return a + b;
    }
};

// 3. 实例化 Lambda 对象
__Lambda_123 lambda;