析构函数中的正确错误处理

时间:2018-08-10 02:26:25

标签: c++ exception destructor raii

这是一种概念问题。

让我们假设我们有一些代码库可以从较高层次上与硬件一起工作,并且整个错误处理机制是由异常实现的。假设我们正在打开/关闭一些阀门。只要该硬件操作具有最终确定程序,我们就需要使用RAII概念。因此,某些foo()过程可能如下所示:

class Valve()
{
public:
    Valve()
    {
        // open valve
    } 
    ~Valve()
    {
        // close valve
        // Potential exception here
    } 
private:
    // valve internal stuff
}

void foo()
{
   try
   {
       Valve v;       
       bar1(v); // <--- throws something
   } catch(...)
   {
        // report error and exit
        // it's guaranteed that valve destructor will be called
   }
}

这段代码看起来不错,但是我们如何管理在阀门关闭期间可能发生的错误。异常无法离开析构函数。我看到的唯一方法是将错误保留在某些错误存储中,例如:

Valve::~Valve()
{
    try
    {
       // close valve
    } catch(...)
    {
        errorStorage.Add(...);
    }
} 

但是这种方法很难看。 有什么想法在这种情况下如何处理?当然,一种方法是根本不使用异常,而是使用返回码方法和一些清除操作(在出错时使用goto)。 / p>

: 本来我想避免这种逻辑重复:

void foo()
{
   try
   {
       Valve v;       
       v.open();    // <- could throw
       bar1(v);     // <- could throw
       v.close();   // <- could throw
   } catch(...)
   {
        if(v.opened())
             v.close(); // kind of logic duplication
   }
}

1 个答案:

答案 0 :(得分:0)

另一种方法是让析构函数直接处理错误。

Valve::~Valve()
{
    try
    {
        // close valve
    } catch(...)
    {
        // handle valve close error
    }
}

诚然,有人可能会提出一个案例,这是不合适的,但是这个问题缺少这种确定的细节。 (如果要找到各种答案,则缺少这些细节可能是一件好事。毕竟,这被自我描述为“概念问题”。)简单记录哪个阀门未能关闭是这种情况的一种情况。应该可以。

总的来说,我希望这种方法可以简化代码整体,因为阀门关闭错误的处理已被定位为Valve类。使用Valve类的代码不需要知道可以关闭阀门,而不必知道关闭阀门时会发生错误,从而改善了数据封装。

自然,此方法确实要求处理阀关闭错误的代码不会引发异常。 (无论如何,这可能是一个好目标,因为它是错误处理的一部分。)


将错误添加到某种错误存储中的计划对我来说似乎是可疑的。我看到“添加”,我认为“可能分配内存”,这意味着“如果内存用完,可能会引发异常”。因此,我想知道是否已采取措施防止此机制抛出异常,而该机制应该延迟异常的抛出。这是否解决了问题,或者只是降低了可能性?