c++ 函数工厂的实现和lambda与function的比较
函数工厂的实现
在 C++ 中,函数工厂是一种设计模式,用于创建对象的实例。通常,函数工厂通过一个工厂函数来创建对象,而不是直接使用 new
关键字。这种方式可以使代码更加灵活,因为工厂函数可以根据需要动态地创建不同类型的对象,而不需要在编译时就确定对象的类型。
下面给出一个示例,展示如何利用 C++ 的 std::function
来实现工厂模式,从而创建不同类型的对象。这个示例中,我们定义了一个基类 Product
,以及两个具体的派生类 ConcreteProductA
和 ConcreteProductB
。然后,我们使用一个 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 |
接口参数、回调机制、函数工厂 |
lambda
和 std::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
✅ 解释:
- 捕获变量
a
是int
,所以 lambda 内部存了一个int
,类大小变为 4 字节 std::function
仍是包装器类型,不随被捕获变量大小变化而变
🛠 汇编角度分析(简化理解)
- lambda 调用:通常编译器内联优化,调用变成普通函数调用或直接加法操作,非常快。
std::function
调用:涉及虚函数机制或函数指针跳转,可能需从堆上解引用包装内容,开销更大。
✅ 对比结果
指标 | lambda | std::function |
---|---|---|
内存占用 | 极小(按捕获变量) | 固定(通常为 32 字节) |
调用性能 | 高(可内联) | 稍慢(间接调用 + 类型擦除) |
使用限制 | 类型固定 | 类型擦除,适合存储多种函数对象 |
总结建议
- 不捕获变量的 lambda:性能极优,可直接传值/内联
- 捕获变量的 lambda:结构简单,仍优于 std::function
std::function
:更通用,适合作为接口传参或容器元素,但要注意性能和堆分配开销