c++ 函数工厂的实现和lambda与function的比较

cpp May 12, 2025

函数工厂的实现

在 C++ 中,函数工厂是一种设计模式,用于创建对象的实例。通常,函数工厂通过一个工厂函数来创建对象,而不是直接使用 new 关键字。这种方式可以使代码更加灵活,因为工厂函数可以根据需要动态地创建不同类型的对象,而不需要在编译时就确定对象的类型。

下面给出一个示例,展示如何利用 C++ 的 std::function 来实现工厂模式,从而创建不同类型的对象。这个示例中,我们定义了一个基类 Product,以及两个具体的派生类 ConcreteProductAConcreteProductB。然后,我们使用一个 std::unordered_map 将标识符映射到对应的创建函数(即对象构造函数封装为 lambda),从而实现对象的动态创建和管理。


示例代码

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <string>

// 基类
class Product {
public:
    virtual void use() = 0;
    virtual ~Product() {}
};

// 具体产品 A
class ConcreteProductA : public Product {
public:
    void use() override {
        std::cout << "使用产品 A" << std::endl;
    }
};

// 具体产品 B
class ConcreteProductB : public Product {
public:
    void use() override {
        std::cout << "使用产品 B" << std::endl;
    }
};

// 定义一个类型别名,用于描述对象创建函数,返回值为 Product 的智能指针
using CreatorFunc = std::function<std::shared_ptr<Product>()>;

// 工厂类,根据输入的 key 来创建对应的产品
class ProductFactory {
public:
    // 注册一个产品创建函数
    void registerProduct(const std::string& key, CreatorFunc creator) {
        creators[key] = creator;
    }
    
    // 根据 key 创建产品对象
    std::shared_ptr<Product> createProduct(const std::string& key) {
        auto it = creators.find(key);
        if (it != creators.end()) {
            // 调用注册的 lambda 创建对象
            return (it->second)();
        }
        return nullptr;
    }
    
private:
    std::unordered_map<std::string, CreatorFunc> creators;
};

int main() {
    ProductFactory factory;
    
    // 通过 lambda 表达式注册产品创建函数,将每个产品的创建过程封装为一个 std::function
    factory.registerProduct("A", []() {
        return std::make_shared<ConcreteProductA>();
    });
    factory.registerProduct("B", []() {
        return std::make_shared<ConcreteProductB>();
    });
    
    // 根据需要创建不同的产品
    auto productA = factory.createProduct("A");
    if (productA) {
        productA->use();  // 输出:使用产品 A
    }
    
    auto productB = factory.createProduct("B");
    if (productB) {
        productB->use();  // 输出:使用产品 B
    }
    
    return 0;
}

关键点解析

  • 使用 std::function
    我们定义了 using CreatorFunc = std::function<std::unique_ptr<Product>()>;。这允许我们将任何能够生成 std::unique_ptr<Product> 的可调用对象(如 lambda 表达式、函数指针等)赋值给 CreatorFunc 变量。这样可以灵活地将不同的构造逻辑封装起来。

  • 注册模式
    工厂类 ProductFactory 内部通过 std::unordered_map 保存了不同产品类型对应的创建函数。调用 registerProduct 方法时,可以将一个产品对应的 lambda 表达式进行注册。当需要创建对象时,只需根据 key 查找并调用对应的函数。

  • 解耦与扩展性
    这种做法将对象的创建和使用解耦,调用者不需要了解具体的构造细节,只需知道产品的标识符即可。如果以后需要扩展新的产品,只需要注册新的 lambda 即可,无需修改现有代码。

  • 智能指针管理
    采用 std::unique_ptr 管理内存,减少了手动管理对象生命周期的风险。


总的来说,这种基于 std::function 的工厂模式非常灵活,既可以根据不同标识符动态创建对象,也可以与其他设计模式(如策略模式)相结合,达到更高的代码扩展性和解耦效果。如果你有更多问题或需要进一步的示例说明,欢迎继续提问。

在 C++ 中,std::function 和 lambda 表达式 是紧密相关但本质不同的概念:


lambda表达式和funtion函数的区别

  • lambda 是一种创建匿名函数对象的语法
  • std::function 是一个通用的函数包装器,可以保存、复制和调用任何可调用对象(包括 lambda)

📌 对比总结表

项目 lambda 表达式 std::function
本质 编译器生成的匿名类对象(闭包) 标准库提供的多态函数封装器
类型 编译时自动推导,具体但复杂类型 明确的统一类型(模板实例)
用途 创建函数对象(可捕获变量) 存储任意函数(可拷贝、赋值)
可否复制 默认不可复制(如果捕获了资源) 可以复制(只要内部对象可复制)
占用空间 更小(无虚函数) 稍大(堆分配,类型擦除)
性能 更快(无类型擦除) 稍慢(有额外开销)

🧪 示例代码

1. Lambda 的使用

auto add = [](int a, int b) {
    return a + b;
};
std::cout << add(2, 3) << std::endl;  // 输出 5

2. std::function 存 lambda

#include <functional>
std::function<int(int, int)> func = [](int a, int b) {
    return a + b;
};
std::cout << func(2, 3) << std::endl;  // 输出 5

3. 捕获变量

int base = 10;
auto lambda = [base](int a) {
    return a + base;
};

std::function<int(int)> f = lambda;  // std::function 也能接收捕获变量的 lambda

⚠️ 性能建议

在性能敏感场景下:

  • 能用 lambda 就用 lambda(无虚函数、无堆分配、编译器优化更好)
  • std::function 在需要 类型擦除、多态存储、参数传递 时使用

✅ 总结

用法推荐 场景
直接用 lambda 性能要求高、函数固定
std::function 接口参数、回调机制、函数工厂

lambdastd::function 的内存差异


🧪 实验代码(无捕获 lambda)

#include <iostream>
#include <functional>

int main() {
    auto lambda = [](int x) { return x + 1; };

    std::function<int(int)> func = lambda;

    std::cout << "sizeof(lambda): " << sizeof(lambda) << std::endl;
    std::cout << "sizeof(std::function): " << sizeof(func) << std::endl;

    return 0;
}

🧾 输出(典型 x86_64 编译器,如 GCC/Clang/MSVC)

sizeof(lambda): 1
sizeof(std::function): 32

解释

  • lambda 本质上是一个空类(无捕获变量时),因此大小为 1 字节(C++ 中空类大小不能为 0)
  • std::function 是一个多态类型,有 虚表指针 + 堆分配函数对象 + 类型擦除管理结构,所以通常是 32 字节左右

有捕获变量的 lambda

int a = 42;
auto lambda_with_capture = [a](int x) { return x + a; };
std::function<int(int)> func2 = lambda_with_capture;

std::cout << "sizeof(lambda_with_capture): " << sizeof(lambda_with_capture) << std::endl;
std::cout << "sizeof(func2): " << sizeof(func2) << std::endl;

🧾 典型输出

sizeof(lambda_with_capture): 4
sizeof(func2): 32

解释

  • 捕获变量 aint,所以 lambda 内部存了一个 int,类大小变为 4 字节
  • std::function 仍是包装器类型,不随被捕获变量大小变化而变

🛠 汇编角度分析(简化理解)

  • lambda 调用:通常编译器内联优化,调用变成普通函数调用或直接加法操作,非常快。
  • std::function 调用:涉及虚函数机制或函数指针跳转,可能需从堆上解引用包装内容,开销更大。

✅ 对比结果

指标 lambda std::function
内存占用 极小(按捕获变量) 固定(通常为 32 字节)
调用性能 高(可内联) 稍慢(间接调用 + 类型擦除)
使用限制 类型固定 类型擦除,适合存储多种函数对象

总结建议

  • 不捕获变量的 lambda:性能极优,可直接传值/内联
  • 捕获变量的 lambda:结构简单,仍优于 std::function
  • std::function:更通用,适合作为接口传参或容器元素,但要注意性能和堆分配开销

zzx

a programmer. github: https://github.com/zzxT