简单工厂不是GoF 23种设计模式之一,而是封装对象创建逻辑的编程习惯;其C++实现核心是用static函数+智能指针(如std::unique_ptr)根据参数返回子类对象,避免裸指针、硬编码类型和手动内存管理,并须声明虚析构函数确保安全销毁。

简单工厂不是设计模式,但它是工厂模式的起点
严格来说,简单工厂 并不在 GoF 23 种设计模式之列,它只是一个“编程习惯”或“封装创建逻辑的技巧”。但它常被当作学习 工厂方法 和 抽象工厂 的跳板。C++ 中实现它的核心是:用一个独立函数或静态方法,根据参数返回不同子类对象的指针(通常是 std::unique_ptr 或 std::shared_ptr),避免在业务代码里直接 new 具体类型。
用 static 成员函数 + 智能指针实现最简可行版
关键点不是“多高级”,而是“不暴露 new、不硬编码类型、能统一管理生命周期”。下面是最常用且安全的做法:
#include#include #include class Product { public: virtual ~Product() = default; virtual void operation() const = 0; };
class ConcreteProductA : public Product { public: void operation() const override { / ... / } };
class ConcreteProductB : public Product { public: void operation() const override { / ... / } };
class SimpleFactory { public: static std::unique_ptr
create(const std::string& type) { if (type == "A") { return std::make_unique (); } else if (type == "B") { return std::make_unique (); } else { throw std::invalid_argument("Unknown product type: " + type); } } };
使用时只需:auto p = SimpleFactory::create("A"); —— 调用方完全不知道 ConcreteProductA 的存在,也不负责 delete。
-
std::unique_ptr是首选:语义清晰(独占所有权)、无引用计数开销 - 不要返回裸指针(
Product*):容易引发内存泄漏或误删 - 参数建议用
const std::string&而非const char*:避免空指针和编码歧义 - 错误处理必须有:否则调用方无法感知非法输入,可能返回空指针导致崩溃
为什么不用 switch?以及 enum 版本更安全的场景
字符串判断在编译期不可检查,运行时才报错。如果产品类型固定且数量少,用 enum 更可靠:
立即学习“C++免费学习笔记(深入)”;
enum class ProductType { A, B };
class SimpleFactory {
public:
static std::unique_ptr create(ProductType type) {
switch (type) {
case ProductType::A: return std::make_unique();
case ProductType::B: return std::make_unique();
default: throw std::invalid_argument("Unknown enum value");
}
}
};
好处是显而易见的:
- 编译器能检查是否覆盖所有枚举值(开启
-Wswitch-enum或/we4061) - 没有字符串比较开销,性能略优
- IDE 可以自动补全,不易拼错
- 适合配置由代码驱动(而非外部 JSON/INI)的场景
容易踩的坑:继承体系没写虚析构函数
这是 C++ 工厂模式中最隐蔽也最致命的问题。如果基类 Product 的析构函数不是 virtual,那么通过 Product* 或 std::unique_ptr 删除对象时,ConcreteProductA 的析构函数根本不会被调用 —— 导致资源泄漏(比如文件句柄、内存池分配的内存等)。
务必确认:
-
Product类中声明了virtual ~Product() = default;(或自定义虚析构函数) - 所有派生类析构函数会自动成为虚函数,无需重复加
virtual - 即使派生类没有资源要清理,也不能省略基类的虚析构
工厂本身不复杂,难的是让整个继承链和生命周期管理真正安全。一旦漏掉虚析构,调试起来往往表现为“看起来正常,但内存缓慢增长”或“析构时崩溃”,而且问题不在工厂代码里,而在使用者对多态销毁的理解上。











