std::function源码实现

std::function 的源码实现涉及类型擦除多态封装资源管理等核心技术,不同标准库(如 libstdc++、libc++)的实现细节略有差异,但核心原理一致。下面通过一个简化的模拟实现,展示其关键组件和工作机制(忽略标准库中的优化细节,如小对象优化 SSO 等)。

核心组件拆解

std::function 的实现主要依赖三个部分:

  1. 抽象基类:定义统一的调用接口(虚函数)。
  2. 派生类模板:继承基类,存储具体可调用对象,并实现基类接口。
  3. function:持有基类指针,管理派生类对象的生命周期,提供统一的调用入口。

简化实现代码

以下是模拟 std::function 的核心代码,支持存储、复制、移动和调用可调用对象:

#include <utility>      // 用于std::forward
#include <algorithm>    // 用于std::swap
#include <stdexcept>    // 用于std::bad_function_call

// 前向声明
template <typename Signature>
class my_function;

// 特化版本:处理函数签名(返回值Ret,参数Args...)
template <typename Ret, typename... Args>
class my_function<Ret(Args...)> {
private:
    // 抽象基类:定义统一接口
    struct function_base {
        virtual ~function_base() = default;
        // 调用接口:纯虚函数,由派生类实现
        virtual Ret operator()(Args... args) const = 0;
        // 克隆自身:用于复制构造
        virtual function_base* clone() const = 0;
    };

    // 派生类模板:存储具体可调用对象
    template <typename F>
    struct function_impl : function_base {
        F func;  // 存储可调用对象(函数、lambda、仿函数等)

        // 构造函数:接收可调用对象并存储
        explicit function_impl(F&& f) : func(std::forward<F>(f)) {}

        // 实现调用:转发参数给存储的可调用对象
        Ret operator()(Args... args) const override {
            return func(std::forward<Args>(args)...);  // 完美转发
        }

        // 克隆自身:创建当前对象的副本(用于my_function的复制)
        function_base* clone() const override {
            return new function_impl<F>(func);  // 复制存储的可调用对象
        }
    };

    function_base* impl = nullptr;  // 基类指针,指向派生类对象

public:
    // 默认构造:空状态
    my_function() noexcept = default;

    // 模板构造:接收任意可调用对象(F需能匹配Ret(Args...)签名)
    template <typename F>
    my_function(F&& f) {
        // 构造派生类对象,存储f的副本
        impl = new function_impl<F>(std::forward<F>(f));
    }

    // 复制构造:通过clone()复制内部实现
    my_function(const my_function& other) {
        if (other.impl) {
            impl = other.impl->clone();  // 深拷贝
        }
    }

    // 移动构造:转移资源所有权
    my_function(my_function&& other) noexcept : impl(other.impl) {
        other.impl = nullptr;  // 源对象置空
    }

    // 复制赋值
    my_function& operator=(const my_function& other) {
        if (this != &other) {
            my_function temp(other);  // 利用复制构造创建临时对象
            swap(temp);               // 交换资源(异常安全)
        }
        return *this;
    }

    // 移动赋值
    my_function& operator=(my_function&& other) noexcept {
        if (this != &other) {
            delete impl;               // 释放当前资源
            impl = other.impl;         // 接管源对象资源
            other.impl = nullptr;      // 源对象置空
        }
        return *this;
    }

    // 析构:释放内部资源
    ~my_function() {
        delete impl;
    }

    // 交换两个my_function的资源
    void swap(my_function& other) noexcept {
        std::swap(impl, other.impl);
    }

    // 调用操作符:转发给内部实现
    Ret operator()(Args... args) const {
        if (!impl) {
            throw std::bad_function_call();  // 空状态调用抛出异常
        }
        return (*impl)(std::forward<Args>(args)...);  // 多态调用
    }

    // 检查是否为空(是否存储了可调用对象)
    explicit operator bool() const noexcept {
        return impl != nullptr;
    }
};

代码解析

1. 抽象基类 function_base
  • 定义了 std::function 所需的统一接口,包括:
    • 虚析构函数:确保派生类对象能被正确销毁。
    • 纯虚函数 operator():接收 Args... 类型参数,返回 Ret 类型,是调用可调用对象的入口。
    • 纯虚函数 clone():用于复制自身(实现 my_function 的复制构造)。
2. 派生类模板 function_impl<F>
  • 模板参数 F 是具体可调用对象的类型(如函数、lambda、仿函数等)。
  • 成员变量 func 存储可调用对象的副本(通过完美转发构造,减少复制开销)。
  • 实现 function_base 的接口:
    • operator():调用 func 并转发参数(完美转发确保参数传递的正确性)。
    • clone():创建 function_impl<F> 的新实例,复制 func(实现深拷贝)。
3. my_function 类(模拟 std::function
  • 核心成员 implfunction_base* 类型,通过多态指向 function_impl<F> 对象(类型擦除的关键)。
  • 构造函数:接收任意可调用对象 F,构造 function_impl<F> 并让 impl 指向它。
  • 复制/移动语义
    • 复制:通过 clone() 深拷贝 impl 指向的对象。
    • 移动:直接转移 impl 的所有权(源对象置空)。
  • 调用操作:通过 impl->operator() 触发多态调用,实际执行 function_impl<F> 中存储的可调用对象。
  • 空状态检查:通过 impl != nullptr 判断是否存储了可调用对象,空状态调用抛出 bad_function_call

使用示例

#include <iostream>
#include "my_function.h"

// 普通函数
int add(int a, int b) { return a + b; }

// 仿函数
struct Multiply {
    int operator()(int a, int b) const { return a * b; }
};

int main() {
    // 存储普通函数
    my_function<int(int, int)> func1 = add;
    std::cout << "add: " << func1(2, 3) << std::endl;  // 输出5

    // 存储lambda表达式
    my_function<int(int, int)> func2 = [](int a, int b) { return a - b; };
    std::cout << "subtract: " << func2(5, 2) << std::endl;  // 输出3

    // 存储仿函数
    my_function<int(int, int)> func3 = Multiply{};
    std::cout << "multiply: " << func3(2, 3) << std::endl;  // 输出6

    // 复制和移动
    my_function<int(int, int)> func4 = func3;  // 复制
    std::cout << "func4: " << func4(3, 4) << std::endl;  // 输出12

    my_function<int(int, int)> func5 = std::move(func4);  // 移动
    std::cout << "func5: " << func5(3, 4) << std::endl;  // 输出12
    std::cout << "func4 is " << (func4 ? "not empty" : "empty") << std::endl;  // 输出empty

    return 0;
}

实际标准库的优化

上述简化实现仅展示核心原理,实际标准库(如 libstdc++)还有以下优化:

  1. 小对象优化(SSO)
    对于小型可调用对象(如无捕获的 lambda、函数指针),直接存储在 std::function 内部的栈上缓冲区,避免动态内存分配(new/delete),提升性能。

  2. 异常安全增强
    实际实现中会更严格地处理异常(如 clone() 过程中抛出异常时的资源释放)。

  3. 支持更多可调用类型
    对成员函数指针、std::bind 返回的绑定器等特殊可调用对象进行适配。

  4. 空状态优化
    可能用空指针或特殊标记表示空状态,减少内存开销。

总结

std::function 的核心实现依赖**“基类接口 + 派生类模板”的多态结构**,通过类型擦除统一封装各种可调用对象。其关键是用基类指针存储具体可调用对象的派生类实例,通过虚函数调用实现多态,对外提供统一的调用接口。

这种设计平衡了灵活性(支持任意可调用对象)和易用性(统一的类型和接口),使其成为 C++ 中处理回调、策略模式等场景的核心工具。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值