gpt4 book ai didi

c++ - 资源句柄 - 禁止默认构造函数?

转载 作者:行者123 更新时间:2023-12-02 03:20:46 26 4
gpt4 key购买 nike

所以,我一直在做一些库的开发,并陷入了困境。图书馆是私有(private)的,所以我不能分享它,但我觉得这可能是一个有意义的问题。

困境本身表现为一个问题,即为什么库中没有资源处理类的默认构造函数。该类处理一个特定的文件结构,这并不重要,但让我们调用类 Quake3File。

然后请求是实现默认构造函数和“适当的”打开/关闭方法。我的思路是 RAII 风格,也就是说,如果您创建类的实例化,则必须为其提供一个它处理的资源。这样做可以确保所有成功构建的句柄都是 有效 和 IMO 消除了一整类错误。

我的建议是保留一个(智能)指针,然后用户不必实现打开/关闭并打开一 jar 蠕虫,而是在免费商店中创建类以“打开”它并在您想要删除时删除“关闭”它。当它超出范围时,使用智能指针甚至会为您“关闭”它。

这就是冲突的来源,我喜欢模仿类的 STL 设计,因为这使我的类更易于使用。由于我正在创建一个主要处理文件的类,如果我以 std::fstream 为指导,那么我不确定是否应该实现默认构造函数。整个 std::fstream 层次结构的事实使我指向是,但我自己的想法是否。

所以问题或多或少是:

  • 资源句柄真的应该有默认构造函数吗?
  • 在处理文件的类上实现默认构造函数的好方法是什么?只需将内部状态设置为无效状态,如果用户通过不为其提供资源而误用它,是否会导致未定义的行为?想要沿着这条路走下去似乎很奇怪。
  • 为什么 STL 使用默认构造函数实现 fstream 层次结构?遗留原因?

  • 希望我的问题被理解。谢谢。

    最佳答案

    可以说有两类 RAII 类:“始终有效”和“可能为空”类。标准库(或接近标准的库,如 Boost)中的大多数类都属于后一类,我将在这里解释几个原因。 “始终有效”是指必须构造为有效状态的类,然后在销毁之前保持有效。我所说的“可能为空”是指可以在无效(或空)状态下构造的类,或者在某个时刻变为无效(或空)的类。在这两种情况下,RAII 原则仍然存在,即类处理资源并实现对其的自动管理,如在销毁时释放资源。因此,从用户的角度来看,他们都享有相同的资源泄漏保护。但是有一些关键的区别。

    首先要考虑的是,我能想到的几乎所有资源的一个关键方面是资源获取总是会失败。例如,您可能无法打开文件、无法分配内存、无法建立连接、无法为资源创建上下文等。因此,您需要一种方法来处理这种潜在的故障。在“始终有效”的 RAII 类中,您别无选择,只能通过从构造函数抛出异常来报告该失败。在“可能为空”的类中,您可以选择通过使对象处于空状态来报告该失败,也可以抛出异常。这可能是 IO-streams 库使用该模式的主要原因之一,因为他们决定将异常抛出作为其类中的一个可选功能(可能是因为许多人不愿过多地使用异常)。

    要考虑的第二件事是“始终有效”的类不能是可移动的类。将资源从一个对象移动到另一个对象意味着使源对象“为空”。这意味着“始终有效”的类必须是不可复制和不可移动的,这可能会给用户带来一些烦恼,并且还会限制您自己提供易于使用的界面的能力(例如,工厂函数等)。这也将要求用户在需要移动对象时在 freestore 上分配对象。

    (编辑)
    正如下面 DyP 所指出的,只要您可以将对象置于可破坏状态,您就可以拥有一个“始终有效”的可移动类。换句话说,该对象的任何其他后续使用都将是 UB,只有销毁才会表现良好。然而,强制执行“始终有效”资源的类仍然不够灵活,并且会给用户带来一些烦恼。
    (结束编辑)

    显然,正如您所指出的,“始终有效”的类通常在其实现中更加万无一失,因为您无需考虑资源为空的情况。换句话说,当你实现一个“可能为空”的类时,你必须在每个成员函数中检查资源是否有效(例如,文件是否打开)。但请记住,“易于实现”并不是规定特定界面选择的正当理由,界面面向用户。但是,这个问题对于用户端的代码也是如此。当用户处理一个“可能为空”的对象时,他总是必须检查有效性,这会变得很麻烦且容易出错。

    另一方面,一个“永远有效”的类将不得不完全依赖异常机制来报告它的错误(即,错误条件不会因为“永远有效”的假设而消失),因此可能会带来一些有趣的挑战在其实现中。通常,您必须为涉及该类的所有函数(包括实现代码和用户端代码)提供强大的异常安全保证。例如,如果您假定对象“始终有效”,并且您尝试执行失败的操作(例如读取文件末尾之外),那么您需要回滚该操作并将对象恢复到其原始有效状态,以强制执行您的“始终有效”的假设。通常,用户会在相关时被迫做同样的事情。这可能与您正在处理的资源类型兼容,也可能不兼容。

    (编辑)
    正如下面 DyP 所指出的,这两种 RAII 类之间存在灰色阴影。所以,请注意,这个解释是在描述两个极点或两个一般分类。我并不是说这是非黑即白的区别。显然,许多资源具有不同程度的“有效性”(例如,无效的文件处理程序可能处于“未打开”状态或“到达文件结尾”状态,这可以以不同的方式处理,即,像“始终打开”,“可能在 EOF”,文件处理程序类)。
    (结束编辑)

    Should resource handles really have default constructors?



    RAII 类的默认构造函数通常被理解为将对象创建为“空”状态,这意味着它们仅对“可能为空”的实现有效。

    What is a good way implement a default constructor on a class that deals with files? Just set the internal state to an invalid one and if a user missuses it by not giving it a resource have it result in undefined behavior? Seems strange to want to take it down this route.



    我遇到的大多数资源都有一种自然的方式来表达“空”或“无效”,无论是空指针、空文件句柄,还是只是一个标记状态为有效与否的标志。所以,这很容易。然而,这并不意味着滥用该类会触发“未定义行为”。设计这样的类绝对是可怕的。正如我之前所说,可能会发生错误条件,并且使类“始终有效”并不会改变这一事实,只会改变您处理它们的方式。在这两种情况下,您都必须检查错误条件并报告它们,并在它们发生时完全指定您的类的行为。你不能只是说“如果出现问题,代码有'未定义的行为'”,你必须指定你的类的行为(一种或另一种方式),以防出现错误情况,句号。

    Why does the STL implement the fstream hierarchy with default constructors? Legacy reasons?



    首先,IO-stream 库不是 STL(标准模板库)的一部分,但这是一个常见的错误。不管怎样,如果你阅读了我上面的解释,你可能会理解为什么 IO-stream 库选择按照它的方式做事。我认为它本质上归结为避免异常作为其实现所需的基 native 制。它们允许将异常(exception)作为一种选择,但不要强制它们,而且我认为这对许多人来说一定是一个要求,尤其是在编写它的时候,可能仍然是今天。

    关于c++ - 资源句柄 - 禁止默认构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20383993/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com