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

Hon*_*onf 8 c++ default-constructor c++11

所以,我一直在做一些图书馆开发并陷入两难境地.图书馆是私人的,所以我无法分享,但我觉得这可能是一个有意义的问题.

这种困境本身就是为什么库中的资源处理类没有默认构造函数的问题.该类处理特定的文件结构,这并不重要,但我们可以调用类Quake3File.

然后请求实现默认构造函数和"适当的"Open/Close方法.我的思路是RAII风格,即如果你创建了一个类的实例化,你必须给它一个它处理的资源.这样做可以确保任何和所有成功构造的句柄都有效,IMO可以消除一整类错误.

我的建议是保持一个(智能)指针,然后不必实现打开/关闭并打开一堆蠕虫,用户在免费商店创建类"打开"它并删除是你想要的时候"关闭"它.当它超出范围时,使用智能指针甚至会"关闭"它.

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

所以问题或多或少都是:

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

希望我的问题得到理解.谢谢.

Mik*_*son 5

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

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

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

(编辑) 正如下面的DyP所指出的,你可以有一个可以移动的"永远有效"的类,只要你可以把对象置于可破坏的状态.换句话说,对象的任何其他后续使用都是UB,只有破坏才会表现良好.然而,仍然存在强制执行"始终有效"资源的类将不那么灵活并且对用户造成一些烦扰. (结束编辑)

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

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

(编辑) 正如下面的DyP所指出的,这两种类型的RAII类之间存在灰色阴影.因此,请注意,此解释描述了两个极端对立或两个一般分类.我并不是说这是一个黑白分明.显然,许多资源具有不同程度的"有效性"(例如,无效的文件处理程序可能处于"未打开"状态或"达到文件结束"状态,这可以被不同地处理,即,像"总是打开","也许在EOF",文件处理程序类). (结束编辑)

资源句柄是否真的有默认构造函数?

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

在处理文件的类上实现默认构造函数有什么好方法?只是将内部状态设置为无效状态,如果用户通过不给它资源而错过它,会导致未定义的行为吗?想要沿着这条路走下去似乎很奇怪.

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

为什么STL使用默认构造函数实现fstream层次结构?遗产原因?

首先,IO流库不是STL(标准模板库)的一部分,但这是一个常见的错误.无论如何,如果您阅读上面的解释,您可能会理解为什么IO流库选择以它的方式执行操作.我认为它基本上归结为避免异常作为其实现的必要的基本机制.它们允许例外作为一种选择,但不要强制使用它们,我认为这对许多人来说一定是必需的,特别是在它编写的日子里,甚至可能在今天.