【发布时间】:2016-08-06 15:01:35
【问题描述】:
我正在研究如何添加 C++ 异常处理来处理现有实时应用程序中的运行时错误。我从构建对象的失败开始,这些对象封装了硬件系统组件的驱动程序,例如位于 Raspberry Pi 平台的 SPI 总线上的电源监控微控制器。
遵循 RAII 原则,我必须在它们的构造函数中完全初始化这些对象,如果系统资源不可用,例如如果 SPI 驱动程序没有加载,这会导致失败的可能性。由于构造函数没有返回值,因此我必须使用异常来处理此类失败。
这些硬件驱动程序对象是静态的(文件范围)。为什么?这样它们的构造函数就会被自动调用,更重要的是,它们的析构函数会在程序退出时自动调用。此外,我需要从主程序文件中的任何位置获取对象,因为它们代表全局硬件资源。
我并不关心异常是如何处理的(我可以在抛出之前发出有用的错误信息),但我关心程序是否正确终止。
发生的情况是,如果静态分配对象的构造函数失败并抛出异常,那么在失败对象之前的词法上其他静态对象的析构函数不会被调用。我已经在一个最小的测试台上对此进行了测试。 Apple、Pear 和 Orange 类只有构造函数和析构函数,它们向标准输出宣布自己,除了 Orange 的构造函数随后抛出异常。在主文件中,我按顺序定义了 Apple、Pear 和 Orange 的一个静态实例。构造函数在程序执行时被调用,Orange 抛出异常,程序结束而不调用 Apple 和 Pear 的析构函数。
我在这里错过了什么?
在类似问题的答案中,例如556655,人们建议: - 不在构造函数中抛出异常。嗯? - 有一个单独的初始化方法,在构造后称为“手动”,以执行任何可能失败的操作。那么RAII呢? (顺便说一句,这就是我现在拥有的东西,没有例外)。 - 将静态对象更改为指针并使用 new 运算符“手动”调用构造函数。然后我必须在失败的情况下调用正确的析构函数,我希望使用异常可以避免这种情况。 - 将每个静态对象包装在另一个对象中,该对象具有访问器函数以获取对其的引用。显然,直到第一次在外部对象上调用访问器时才会调用内部对象的构造函数,这将允许我捕获异常,并且可能会导致整洁的退出。这似乎是一个可怕的组合。
回想一下,我不需要捕获异常,程序可以随意终止。这使我的问题与我发现的其他问题不同。我的问题是,为什么不为成功构造的静态对象调用析构函数?
格雷厄姆。
【问题讨论】:
-
我们又来了。全局变量,静态全局变量(接下来:单例 - arrrgh;痛苦,很痛苦)。如果你想避免很多痛苦:不要使用全局变量,甚至文件范围的静态变量。只是不要。
-
我理解您对使用文件范围变量的反感。我尽我所能避免它们,但在这个程序中,我无法弄清楚如何以任何合理的方式做到这一点。我有封装只读命名管道、只写命名管道和三个硬件子系统驱动程序的文件范围对象。命令通过一个管道到达,我操纵硬件执行命令并通过另一个管道生成响应。我必须跟踪硬件的状态,以使在某些操作完成之前无法执行的命令失败。
-
[继续] ...如果我不将驱动程序对象设为全局(或将它们打包在全局包装器对象中,我认为这不能解决问题)我必须传递它们通过命令解析和执行函数的层次结构作为参数(除非我遗漏了什么并且已经有几十年了)。
-
@GrahamDavies:您可以使用全局指针而不是全局对象,并在 main 中初始化它们。这将完全避免整个静态初始化混乱。你仍然会有丑陋的全局变量,但你可以稍后再决定:)
标签: c++ exception constructor static