惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
Microsoft Azure Blog
Microsoft Azure Blog
Engineering at Meta
Engineering at Meta
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
小众软件
小众软件
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
I
InfoQ
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
MyScale Blog
MyScale Blog

博客园 - 新西兰程序员

C++ 构造函数中的初始化参数列表initializer list C++中的线程thread,以及C++11中的std::atomic学习 Leetcode56 Merge Intervals 合并区间 -- C++实现 C++中的内存对齐 C++中的static关键字使用以及全局变量 C++17中的结构化绑定Structured Binding C++中的函数参数是指针类型, 是按值传递 C++中异常处理机制中的栈展开stack unwinding C++实现链表反转 Linux中的性能分析工具 perf 来分析C++性能 C++17中新建一个类时,编译器默认生成的类成员函数 QT中的元对象系统 Leetcode 114 - 二叉树展开为链表 Leetcode65 有效数字 - 判断给定的字符串是否是一个有效数字 LettCode2289-Steps to Make Array Non-descreasing C#中的委托详解 C++中的仿函数Functor C++中的const和constexpr异同比较 C++中的左值和右值,以及右值引用,移动语义 C++中传递参数是指针类型以及传入参数是指针的指针(**)详解 C++中GetTickCount函数学习 C++中以类的成员函数作为Windows callback函数需要设置成static函数 C#中的System.Security.SecureString学习 C++中的悬挂指针和野指针 C++中四种不同的对象生存方式(in stack, in heap, global, local static) C#中使用Parallel类来进行多线程并发编程 String类型转LPCTSTR -----理解C++中的字符串类型转换 C++中的虚函数和虚函数表 C++中基类指针指向派生类对象 数据结构 - 栈的学习
C++中的std::function
新西兰程序员 · 2024-12-02 · via 博客园 - 新西兰程序员

std::function()是C++标准库中的一个通用多态函数包装器, 它可以存储,复制和调用任意可调用目标(函数,lambda表达式,绑定表达式或其他函数对象).

std::function占用固定尺寸的内存 . 它允许我们将可调用对象(函数,函数指针,Lambda表达式, std::bind以及其他函数对象) 包装成一个对象, 使得我们可以像操作其他对象一样操作和传递可调用对象

std::function是C++引入的标准库和组件,它位于 <functional>头文件中,  它主要作用就是将可调用对象封装成一个函数对象,提供一种统一的方式来处理各种类型的可调用对象

std::function的基本语法如下

std::function<返回类型(参数类型1,参数类型2,....)>func;

1. 封装函数

#include <iostream>
#include <functional>

void greet()
{
   std::cout << "Hello, Beijing!" << std::end1;
}

int main()
{
    std::function<void()> func = greet;
   func();  //调用封装的函数
   return 0;
}

2. 封装Lambda表达式

#include <iostream>
#include <functional>

int main()
{
     std::function<void()> func = [](){
           std::cout << "Lambda function test!" << std::end1;
      };

      func(); //调用封装的lambda表达式
     return 0;
}

3. 封装可调用对象 (仿函数)

#include <iostream>
#include <functional>

class Greet
{
    public:
        void operator()() const{
            std::cout << "functor test!" << std::end1;
        }
};

int main()
{
     std::function<void()> func = Greet();
     func(); //调用封装的仿函数
     return 0;
}

4. 类的成员函数和类的静态函数

Class MyClass
{
    public:
        //类的成员函数
        int MyClassMember(int x)  {return x;}

        //类的静态函数
        static int MyStaticMember(int y)  {return y;}
}


int main()
{
     //类的成员函数,需要先bind到具体的类
     MyClass myObj;
     
     std::function<int(int)> func = std::bind(&myObj::MyClassMember,myObj,std::placeholders::_1);  //_1表示占位符,调用时传参数
   int result = func(10);

   
   //类的静态函数,可以认为是普通函数,和调用普通函数一样
   func = MyClass::MyStaticMember;
   result = func(50);

   return 0;
  

}

看完上面这几个例子,我们来了解一下std::function的实现原理

 std::function通常使用类型擦除和小对象优化(Small Object Optimization, SOO) 来实现

 类型擦除

std::function使用类型擦除来存储不同类型的可调用对象,这意味着它通过一个固定大小的存储空间和一个指向这些对象的虚表(vtable)来实现多态性

类型擦除允许std::function在运行时处理各种不同的类型,而不需要知道这些类型的细节

小对象优化

对于小对象(通常是指尺寸较小可以直接存储在std::function内部的对象), std::function会直接在它内部存储这些对象. 这就避免了动态内存分配的开销

对于大对象(超出 std::function内部存储容量的对象), std::function会在堆上分配内存,同时在std::function内部存储一个指向这些对象的指针

std::function总是占有固定尺寸的内存, 它的内部通常包含以下几个部分:

  • 一个指向实际存储对象的指针和存储小对象的内部缓冲区
  • 一个指向虚表的指针,用于多态调用
  • 一些额外的元数据,用于管理存储和调用

所以,不管存储对象多大或者多小,std::function的实例总是占用固定大小的内存,以包含这些指针和元数据

总之,std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的). 用std::function来实现回调函数,在函数形参中代替函数指针,更安全也更可视化