直接返回 std::unique_ptr 是最稳妥的选择;裸指针易致内存泄漏,栈对象引用会导致悬垂引用;抽象工厂仅适用于需配套对象族的场景;应使用 new (std::nothrow) 并检查空指针;模板工厂需用完美转发支持任意构造参数。

工厂函数返回指针还是引用?
直接返回 std::unique_ptr 是当前最稳妥的选择。裸指针(T*)容易引发内存泄漏,而返回栈对象引用(T&)在工厂内部构造临时对象时会触发悬垂引用——这是新手掉进最多的一次坑。
常见错误现象:Segmentation fault 或未定义行为,调试时发现对象在函数返回后已被析构。
- 永远别写
return &createObject();这类代码 - 若必须返回引用,确保对象生命周期由外部管理(比如传入一个
std::vector<t>&</t>容器来持有) - 用
std::unique_ptr语义清晰,且能配合多态:基类指针指向派生类实例,析构自动调用虚析构函数
抽象工厂 vs 简单工厂:该选哪个?
90% 的业务场景里,你其实只需要简单工厂——也就是一个带 switch 或 if-else 的静态函数,根据字符串或枚举创建具体类型。抽象工厂(多个工厂方法 + 工厂接口)只在需要“一整套配套对象”时才值得引入,比如同时创建 Button、Checkbox 和 Dialog 的 Windows / macOS 两套实现。
性能影响:抽象工厂多一层虚函数调用和对象分配;简单工厂编译期可内联,更轻量。
立即学习“C++免费学习笔记(深入)”;
- 输入是单一类型标识(如
"json"、FileType::XML)→ 用简单工厂 - 需要隔离产品族(如“日志模块 + 配置模块 + 缓存模块”的本地版 / 分布式版)→ 抽象工厂才有意义
- C++ 没有接口关键字,抽象工厂的“接口”就是含纯虚函数的基类,记得加
virtual ~Factory() = default;
如何避免 new 失败导致的异常中断?
new 在内存不足时默认抛出 std::bad_alloc,但很多嵌入式或服务端场景要求失败可预测、不抛异常。这时得用 new (std::nothrow),并手动检查指针是否为空。
容易踩的坑:忘了检查返回值,或者把 std::nothrow 写成 std::no_throw(编译不过)、nothrow(少 std:: 命名空间)。
- 正确写法:
auto ptr = new (std::nothrow) DerivedClass(); if (!ptr) return nullptr; - 搭配
std::unique_ptr时,构造失败会返回空的智能指针,不用额外判空 - 如果项目禁用异常(
-fno-exceptions),必须用std::nothrow版本,否则new行为未定义
模板工厂怎么支持不同构造参数?
硬编码固定参数列表(比如只接受 int)会让工厂很快僵化。C++17 起,用参数包转发(Args&&...)+ 完美转发是最通用解法。
关键点在于:工厂内部不能直接调用 new T(args...),而要用 new T(std::forward<args>(args)...)</args>,否则右值会被转成左值,移动语义失效。
- 模板工厂函数签名典型长这样:
template<typename t typename... args> std::unique_ptr<t> create(Args&&... args);</t></typename> - 如果某类构造函数是
explicit,模板推导仍正常,无需特殊处理 - 注意:变参模板无法推导初始化列表(
{1,2,3}),这种场景需单独重载或改用工厂类 + 配置结构体
工厂模式真正的复杂点不在结构,而在对象生命周期边界的划定——谁 delete、谁 move、谁 copy、谁 hold reference,这些决定比“怎么写 virtual Create()”重要得多。漏掉一个 virtual 析构函数,或误传了栈对象地址,后面查半天堆栈也看不出源头。










