【发布时间】:2021-05-29 09:03:09
【问题描述】:
我过去主要认为 RAII 是关于使用对象生命周期来避免资源泄漏,这在实践中对我很有帮助。但我最近讨论了 RAII 模式究竟由什么构成,什么不是,这让我在网上搜索了更多的定义和评论,最终导致更多的混乱而不是清晰。
RAII 类的标准定义似乎需要两个属性:
- RAII 类的构造函数应获取资源,如果在该过程中失败则抛出异常。
- RAII 类的析构函数应该释放资源。
但是我也看到在一些 RAII 定义中提到,资源所有权可以在此类 RAII 类的实例之间“安全地转移”。因此,资源所有权转移似乎被接受为 RAII 模式的一部分。
但是,资源所有权转移似乎也导致破坏了似乎定义 RAII 的那两个属性。
假设我有两个 RAII 类实例 - Instance_Source 和 Instance_Destination - 我将基础资源的所有权从 Instance_Source 转移到 Instance_Destination。然后我们有:
- 与属性 2 冲突:
-
Instance_Source的析构函数不会释放任何资源,所以我们现在打破了析构函数应该释放资源的要求。
-
- 与属性 1 的冲突:
- 在初始化中获取资源的一个优点是我可以使用知道它们包含有效资源的实例(否则它们不会被构造)。但是随着所有权转移,我现在留下的实例不再包含有效资源,这消除了在构造函数中获取资源应该带来的主要优势。
- 在某些情况下,我不希望
Instance_Destination在其构建期间获取任何资源,因为我希望它仅在特殊条件下获得Instance_Source获取的资源的所有权。为了支持这样的场景,我必须打破构造函数获取资源的要求,允许在不获取任何资源的情况下初始化Instance_Destination。
所以在我需要允许资源所有权转移的场景中,我发现我必须“放松”关于在构造函数中获取资源并在析构函数中释放它们的 2 RAII 要求。而且这在实践中运行得很好,但它在理论上仍然构成 RAII 模式吗?
这就是我提出问题的原因:RAII 支持资源所有权转移吗?
如果答案是是,那么看起来大多数 RAII 定义应该被重新设计,以不依赖于构造函数和析构函数应该对资源做什么。
如果答案是否,那么这应该被强调为 RAII 的一个重要限制。
【问题讨论】:
-
unique_ptr 是一个 RAII 应用程序,它允许移动它拥有的东西:stackoverflow.com/questions/29372976/…
-
我想说
std::string也跟随 RAII,它使用移动语义很好地传输内部指针。关键是 some 实例将始终“拥有”资源并在销毁时释放它。它不一定是首先分配/获取资源的实例。 -
@nahzor:也就是说,
unique_ptr不是 RAII 的完美示例;它还允许您提取资源而不将其转移到另一个 RAII 容器(您可以删除指针并自行管理它;它也可以从它不“获取”的现有指针构造”)。允许获取的资源不受控制,这违反了 RAII 原则。std::string是一个更好的例子;您可以将所有权转让给另一个string,并且您可以查看原始内容,但您不能假设将资源完全从 RAII 管理中移除(不是以任何理智的方式)。 -
基于@ShadowRanger 的评论,也许我应该问一个不同的问题:RAII 的核心原则是什么?在大多数关于 RAII 的讨论中仍然会提到构造函数中的资源获取和析构函数中的资源释放,尽管大多数人似乎认为它们不是必需的。我认为 bolov 的回答明确了一个更重要的原则,即 资源所有权,但为什么所有关于 RAII 的讨论都没有明确指出不太重要的方面?
-
@ShadowRanger:这是有道理的。谢谢!