工厂模式中是否应避免使用new关键字进行内存分配?
工厂模式下的内存安全优化方案
1. 强制返回智能指针,把所有权绑定到类型上
没必要完全避免new,但可以把工厂的返回值从裸指针改成智能指针,从语法层面强制调用者管理内存:
- 如果实例是独占所有权的场景,返回
std::unique_ptr<T>,工厂内部用std::make_unique(比直接new更安全,避免异常引发的内存泄漏); - 如果需要多对象共享实例,返回
std::shared_ptr<T>,搭配std::make_shared还能减少内存分配次数。
示例代码:
// 改造前的工厂(返回裸指针) class FooFactory { public: static Foo* create() { return new Foo(); } }; // 改造后的工厂(返回unique_ptr) class FooFactory { public: static std::unique_ptr<Foo> create() { return std::make_unique<Foo>(); // 也可以写 return std::unique_ptr<Foo>(new Foo()); 但make_unique更推荐 } };
这种方式下,调用者想拿到裸指针必须显式调用get(),属于主动行为,责任清晰,不会出现无意识的内存泄漏。
2. 封装销毁逻辑,禁止外部直接释放实例
如果暂时没法全量替换成智能指针,可以修改产品类的析构函数权限,让只有工厂能销毁实例,强制调用者用工厂提供的销毁方法:
class Foo { // 把析构设为protected,只有友元或子类能调用 protected: ~Foo() = default; friend class FooFactory; }; class FooFactory { public: static Foo* create() { return new Foo(); } // 提供专属销毁方法 static void destroy(Foo* ptr) { if (ptr) delete ptr; } };
这样调用者拿到裸指针后,自己没法直接delete,必须调用FooFactory::destroy,从流程上约束了内存释放的动作,减少遗忘概率。
3. 用对象池托管实例生命周期
如果实例是可复用的,或者需要集中管理生命周期,可以搞个对象池。工厂(池)负责创建、回收、销毁所有实例,调用者只拿临时使用权:
class FooPool { private: std::vector<std::unique_ptr<Foo>> m_pool; std::mutex m_mtx; public: // 获取实例 Foo* acquire() { std::lock_guard<std::mutex> lock(m_mtx); if (!m_pool.empty()) { auto ptr = m_pool.back().release(); m_pool.pop_back(); return ptr; } return new Foo(); } // 归还实例(由池负责后续销毁) void release(Foo* ptr) { if (!ptr) return; std::lock_guard<std::mutex> lock(m_mtx); m_pool.emplace_back(ptr); } // 池销毁时自动清理所有实例 ~FooPool() = default; };
这种方式下,调用者不需要关心销毁,只需要用完归还给池,内存泄漏的风险直接从调用端转移到池的实现端,更可控。
总的来说,工厂模式不需要刻意避免new,核心是把内存管理的责任从“依赖调用者自觉”变成“由代码结构强制约束”。上面几种方案可以根据现有代码的情况逐步落地,比如先从核心模块替换智能指针,再对遗留模块封装销毁逻辑,最后对高频复用的实例搞对象池。
内容的提问来源于stack exchange,提问作者CJ_Notned
相关产品推荐
相关产品推荐

