如何统一处理返回unique_ptr的工厂函数与全局对象引用获取函数?
C++场景下的对象选择方案
不可修改的基础代码
首先存在以下不可修改的代码:
全局Foo对象定义
struct Foo { int i; ~Foo() { std::cout << "gone: " << i << std::endl; } }; Foo globalFoo{7};
获取全局对象的函数
Foo& getFoo() { return globalFoo; }
创建新对象的工厂函数
std::unique_ptr<Foo> makeFoo() { return std::make_unique<Foo>(5); }
客户端需求
在客户端代码中,需要根据运行时输入的布尔值b,决定使用全局对象(通过getFoo()获取)还是创建新的Foo对象(通过makeFoo()获取),代码框架如下:
int main() { bool b{false}; std::cin >> b; /* type */ foo = b ? /* via getFoo */ : /* via makeFoo */; }
不可行的方案分析
以下两种常见做法均存在问题:
方案1:直接使用引用导致悬垂
auto& foo = b ? getFoo() : *makeFoo();
问题:makeFoo()返回的临时unique_ptr会在表达式结束后立即销毁,其管理的Foo对象也会被随之销毁,导致foo变成悬垂引用。
方案2:使用release()导致内存泄漏
auto& foo = b ? getFoo() : *makeFoo().release();
问题:调用release()会让unique_ptr放弃对Foo对象的所有权,若后续不手动调用delete,该对象会发生内存泄漏;手动调用delete又会增加代码复杂度,且容易遗漏。
合适的处理方式
可以通过一个局部的unique_ptr来管理动态创建的对象,同时用引用指向目标对象,确保两者生命周期一致:
int main() { bool b{false}; std::cin >> b; std::unique_ptr<Foo> owned_foo; Foo& foo = b ? getFoo() : *(owned_foo = makeFoo()); // 这里可以正常使用foo }
- 当
b为true时,foo直接引用全局对象,owned_foo保持为空,不会产生额外内存管理操作。 - 当
b为false时,makeFoo()返回的unique_ptr被赋值给owned_foo,foo引用该动态创建的对象;owned_foo会在main函数结束时自动销毁其管理的Foo对象,避免内存泄漏,同时保证foo在整个main函数内都是有效的引用。
另一种更类型安全的方案是使用std::variant来存储两种可能的对象持有方式:
#include <variant> #include <functional> // 用于std::reference_wrapper int main() { bool b{false}; std::cin >> b; std::variant<std::reference_wrapper<Foo>, std::unique_ptr<Foo>> foo_container = b ? std::ref(getFoo()) : makeFoo(); // 通过std::visit获取统一的Foo引用 Foo& foo = std::visit([](auto&& val) -> Foo& { if constexpr (std::is_same_v<std::decay_t<decltype(val)>, std::reference_wrapper<Foo>>) { return val.get(); } else { return *val; } }, foo_container); // 正常使用foo }
这种方式明确区分了"引用全局对象"和"持有动态对象"两种状态,类型安全性更高,适合复杂场景。
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

