我正在弄清楚如何添加C ++异常处理来处理现有实时应用程序中的运行时错误。我从构造对象的失败开始,这些对象封装了硬件系统组件的驱动程序,例如位于Raspberry Pi平台的SPI总线上的电源管理器微控制器。
遵循RAII原则,我必须在其构造函数中完全初始化这些对象,如果系统资源不可用,则可能导致失败,例如,如果SPI驱动程序未加载。由于构造函数没有返回值,我必须使用异常处理此类失败。
这些硬件驱动程序对象是静态的(文件范围)。为什么?这样就可以自动调用它们的构造函数,更重要的是,它们的析构函数会在程序退出时自动调用。此外,我需要从主程序文件中的任何位置获取对象,因为它们代表全局硬件资源。
我真的不在乎如何处理异常(我可以在抛出之前发出有用的错误信息),但我确实关心程序是否正常终止。
发生的情况是,如果静态分配的对象的构造函数失败并抛出异常,则不会调用在失败对象之前在词法上的其他静态对象的析构函数。我在最小的试验台上测试了这个。类,Apple,Pear和Orange只有构造函数和析构函数,只能向stdout宣布自己,除了Orange的构造函数然后抛出异常。在主文件中,我按顺序定义了Apple,Pear和Orange的一个静态实例。构造函数在程序执行时调用,Orange抛出异常并且程序结束而不调用Apple和Pear的析构函数。
我在这里错过了什么?
在类似问题的答案中,例如556655,人们建议: - 不在构造函数中抛出异常。咦? - 具有单独的初始化方法,在构造之后称为“手动”,以执行可能失败的任何操作。那么RAII怎么样? (顺便说一下,这就是我现在拥有的东西,没有例外)。 - 将静态对象更改为指针,并使用new运算符“手动”调用构造函数。然后我必须在发生故障时管理调用正确的析构函数,我希望使用异常可以避免。 - 将每个静态对象包装在另一个具有访问器功能的对象中,以获取对它的引用。显然,直到第一次在外部对象上调用访问器时才会调用内部对象的构造函数,这将允许我捕获异常,并且可能会导致整齐的退出。这似乎是一个可怕的kludge。
回想一下,我不需要捕获异常,程序可以终止它想要的。这使我的问题与我发现的其他问题不同。我的问题是,为什么析构函数不能调用成功构造的静态对象?
格雷厄姆。
答案 0 :(得分:4)
您错过了未处理的例外情况。当程序由于未处理的异常而终止时,您无法保证调用本地和静态对象的析构函数(因为我记得它的实现已定义)。一种解决方案是将这些对象放在一个公共的包装器对象中,在try
中的main
块中创建它,然后只需为它提供一个指向它的指针即可获得当前的任意访问权限。全局变量。
草图:
class Drivers
{
friend auto main() -> int;
// ...
};
namespace impl {
Drivers* p_drivers;
} // namespace impl
auto drivers() -> Drivers& { return *impl::p_drivers; }
auto main()
-> int
{
try
{
Drivers drivers;
impl::p_drivers = &drivers;
// ...
return EXIT_SUCCESS;
}
catch( exception const& x )
{
log_failure( x.what() );
}
return EXIT_FAILURE;
}