C++中工厂模式核心是用静态工厂函数返回智能指针(如std::unique_ptr),基类析构函数需为virtual,避免裸指针和过度抽象;简单工厂适用于单一产品类型,抽象工厂仅用于一族相关产品的协同创建。

工厂模式在 C++ 中的核心实现方式
工厂模式本质是把对象创建逻辑从调用方剥离,交由专门的“工厂”类或函数负责。C++ 中最常用、最轻量的是**静态工厂函数**(返回 std::unique_ptr 或 std::shared_ptr),而非必须定义抽象工厂类——尤其当产品类型不多、继承结构简单时,过度抽象反而增加耦合和编译依赖。
关键点:工厂返回智能指针,而非裸指针;基类析构函数必须为 virtual;工厂内部用 new 或 std::make_unique 构造具体子类。
- 若产品类无状态、可拷贝,也可返回值对象(避免堆分配),但需确保移动语义完备
- 工厂函数通常声明为
static成员或自由函数,不依赖工厂实例状态 - 避免在工厂中做复杂初始化逻辑,那属于产品类自身职责
用 std::unique_ptr 实现简单工厂的典型写法
这是现代 C++ 最推荐的方式:资源自动管理、无内存泄漏风险、语义清晰。假设有一个基类 Shape 和两个派生类 Circle、Rectangle:
class Shape {
public:
virtual ~Shape() = default; // 必须!否则 delete base* 会未定义行为
virtual void draw() const = 0;
};
class Circle : public Shape {
public:
void draw() const override { / ... / }
};
class Rectangle : public Shape {
public:
void draw() const override { / ... / }
};
// 工厂函数
std::unique_ptr createShape(const std::string& type) {
if (type == "circle") return std::make_unique();
if (type == "rectangle") return std::make_unique();
return nullptr; // 或抛出 std::invalid_argument
}
调用方无需关心内存释放:auto s = createShape("circle"); s->draw(); —— 出作用域自动析构。
立即学习“C++免费学习笔记(深入)”;
- 不要用
new Circle后手动转unique_ptr,绕过std::make_unique失去异常安全保证 - 若需传递构造参数(如
Circle(double r)),工厂函数参数应直接接收,而非用 map 存配置再反射构造 - 返回
nullptr时,调用方必须判空;更健壮的做法是抛异常或用std::optional<:unique_ptr>>
什么时候该上抽象工厂(AbstractFactory)?
抽象工厂适用于「一族相关产品」需要被一起创建的场景,比如不同 GUI 框架下的 Button + Checkbox 组合(WindowsFactory vs MacFactory)。单个产品类型(如只造 Shape)用简单工厂足矣,强行套抽象工厂只会让代码膨胀、头文件依赖变重。
典型信号:createButton() 和 createCheckbox() 总是成对出现,且它们之间存在隐式契约(例如尺寸适配、事件响应风格一致)。
- 抽象工厂本身通常是接口类(纯虚函数),具体工厂继承它并实现各创建方法
- 客户端代码依赖抽象工厂接口,不感知具体实现 —— 这才有运行时切换整套 UI 风格的意义
- 若只是想隔离 new,但产品间无协作关系,用简单工厂 + 枚举/字符串分发更直白
容易踩的坑:类型擦除与模板工厂的误用
有人试图用模板写泛型工厂(如 template),这看似灵活,实则破坏了工厂的核心价值:**统一创建入口 + 运行时决策**。模板工厂在编译期就确定类型,无法根据用户输入(如配置文件、网络请求)动态选型。
另一个常见错误是用 void* 或 std::any 做类型擦除后返回,再靠外部强制转型 —— 这等于放弃类型安全,和 C 的函数指针工厂没区别,还更难调试。
- 工厂返回类型必须是明确的基类指针或接口类型,让多态机制生效
- 若真需支持任意类型,应使用类型索引(如
std::variant)配合访问者模式,而非抹掉类型信息 - 工厂类的头文件不应包含所有产品类的完整定义,只需前向声明 +
#include对应的 .cpp 实现文件
工厂模式的复杂度不在语法,而在判断“哪些创建逻辑值得抽取”以及“边界划在哪”。多数业务代码里,一个带字符串分发的 std::unique_ptr 工厂函数,比一整套抽象工厂类更易维护、更少出错。










