将 unique_ptr 传递给函数
-
13-12-2019 - |
题
我正在尝试“现代化”一些现有代码。
- 我有一个类,当前有一个成员变量“Device* device_”。
- 它在一些初始化代码中使用new创建实例,并在析构函数中具有“delete device_”。
- 调用该类的成员函数 许多 其他以 Device* 作为参数的函数。
这很好用,但是为了“现代化”我的代码,我认为我应该将变量定义为 "std::unique_ptr<Device> device_"
并删除对删除的显式调用,这使得代码更安全并且总体上更好。
我的问题是这样的——
- 那么我应该如何通过 设备_ 变量到所有需要它作为参数的函数?
我可以调用 .get 来获取每个函数调用中的原始指针。但这看起来很难看,并且浪费了一些首先使用 unique_ptr 的理由。
或者我可以改变 每一个 函数现在不再采用“Device*”类型的参数,而是采用“std::unique_ptr&”类型的参数。(对我来说)这在某种程度上混淆了函数原型,并使它们难以阅读。
对此的最佳实践是什么?我是否错过了其他选择?
解决方案
在 现代的 C++风格,有两个关键概念:
- 所有权
- 无效性
所有权 是关于某个对象/资源的所有者(在本例中是 Device
)。各种种类 std::unique_ptr
, boost::scoped_ptr
或者 std::shared_ptr
是关于所有权的。
无效性 然而要简单得多:它只是表达给定的对象是否可能为空,并且不关心其他任何事情,当然也不关心所有权!
你之前是 正确的 推动你的班级的实施 unique_ptr
(一般来说),但如果您的目标是实现 PIMPL,您可能需要具有深层复制语义的智能指针。
这清楚地表明您的类是这块内存的唯一负责人,并且巧妙地处理了内存可能泄漏的所有各种方式。
另一方面,大多数 用户 资源的所有权并不关心它的所有权。
只要函数不保留对对象的引用(将其存储在映射或其他东西中),那么重要的是对象的生命周期超过函数调用的持续时间。
因此,选择如何传递参数取决于其可能的情况 无效性:
- 永远不为空?通过一个 参考
- 可能为空?通过一个 指针, ,一个简单的裸指针或类似指针的类(例如,带有 null 陷阱)
其他提示
这确实取决于。如果一个函数必须拥有 unique_ptr 的所有权,那么它的签名应该采用 unique_ptr<Device>
乙肝病毒 价值 并且调用者应该 std::move
指针。如果所有权不是问题,那么我将保留原始指针签名并使用传递指针 unique_ptr get()
. 。这并不难看 如果 相关函数不会接管所有权。
我会用 std::unique_ptr const&
. 。使用非常量引用将使被调用函数可以重置指针。
我认为这是一个很好的方式来表达您被调用的函数可以使用指针,但不能使用其他任何东西。
所以对我来说这将使界面更容易阅读。我知道我不必摆弄传递给我的指针。
最佳实践可能是不使用 std::unique_ptr
在这种情况下,尽管取决于。(通常,您不应有一个以上的原始指针,指向类动态分配的对象。虽然这也取决于。)在这种情况下,您不想做的一件事是通过 std::unique_ptr
(正如你所注意到的,std::unique_ptr<> const&
有点笨拙和混乱)。如果这是对象中唯一动态分配的指针,我只是坚持使用原始指针, delete
在析构函数中。如果有几个这样的指针,我会考虑将它们降级到单独的基类(它们仍然可以是原始指针)。
这对你来说可能不可行,但可以替换每次出现的情况 Device*
经过 const unique_ptr<Device>&
是一个好的开始。
显然你无法复制 unique_ptr
s 并且您不想移动它。替换为引用 unique_ptr
允许现有职能机构继续工作。
现在有一个陷阱,你必须通过 const &
防止被调用者做 unique_ptr.reset()
或者 unique_ptr().release()
. 。请注意,这仍然将可修改的指针传递给设备。使用此解决方案,您没有简单的方法来传递指针或引用 const Device
.