作者:哈喽随风amy | 来源:互联网 | 2023-10-11 10:14
我过去主要认为RAII是关于使用对象生命周期来避免资源泄漏,这在实践中对我来说已经足够好了。但是我最近讨论了什么是RAII模式,什么不是,这让我在网上搜索了更多的定义和评论,结果增
我过去主要认为 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 是否支持资源所有权转移?
如果答案是yes,那么看起来大多数 RAII 定义应该重新设计,不依赖于构造函数和析构函数应该对资源做什么。
如果答案是否定的,那么这应该被强调为 RAII 的一个重要限制。
回答
RAII 是否支持资源所有权转移?
可以,是的。
但是,似乎资源所有权转移也会导致打破似乎定义 RAII 的那两个属性。
有点取决于人们如何定义 RAII 的细节。
解决方案是扩展问题中显示的 RAII 的定义,以允许表示空状态。如果存在空状态的表示,则可以通过将源 RAII 对象保留在这种空状态来移动资源的所有权。
构建和销毁问题中给出的定义对此进行调整是微不足道的:
- 构造要么获取资源,要么初始化为空状态。从技术上讲,这不是必需的,但如果允许空状态,则允许默认构造很方便。
- 析构函数释放资源当且仅当它拥有任何资源。
- 如前一段所述的移动的附加定义。
标准库中的大多数 RAII 类都有空状态的表示,并且那些支持传输它们的资源。此类 RAII 类及其空状态的典型示例:
- 任何动态容器 - 不包含任何元素(并且有空容量)的容器
- 任何智能指针 - 空值
- std::fstream - 与文件无关的流
- std::thread - 与线程无关的包装器
标准库也有 RAII 类,它们没有空状态的表示,因此不能支持资源的传输。这种类的一个例子是std::lock_guard
.
我希望有人也可以提供一个历史的观点
我所拥有的定义的最古老来源是 Stroustrup 的书“C++ 编程语言第 3 版”。根据维基百科的估计,RAII 是在 1984-89 年左右开发的,所以到这本书出版时,它已经是一个 8-13 岁的想法。以下是最相关的部分,希望不会太多侵犯版权:
14.4.1 使用构造函数和析构函数
使用本地对象管理资源的技术通常被称为“资源获取即初始化”。这是一种通用技术,它依赖于构造函数和析构函数的属性以及它们与异常处理的交互。
...
构造函数试图确保其对象被完整且正确地构造。当无法实现时,编写良好的构造函数会尽可能地将系统状态恢复到创建之前的状态。
14.4.2 Auto_ptr
... auto_ptr,支持“资源获取即初始化”技术。
鉴于它std::auto_ptr
不一定拥有资源,因此它的析构函数在这种情况下不会释放资源,它可以将资源转移到另一个实例,而创造 RAII 的作者认为std::auto_ptr
“支持 RAII”,我有信心说与问题中描述的属性相冲突并不取消 RAII 的资格。
请注意,std::auto_ptr 已被 C++11 中移动语义的引入所淘汰,此后已从语言中删除。
E.3.5.3 延迟资源获取
...每当类的语义不要求延迟资源获取时,就应该在构造函数中获取资源。
我没有发现关于 RAII 如何与转移资源所有权的能力相关的明确描述。我怀疑在为具有移动语义的语言编写的后续版本中可能会更多地讨论它。