【问题标题】:Exception in destructor析构函数中的异常
【发布时间】:2011-10-29 17:10:17
【问题描述】:

在我的析构函数中,我必须清理一些资源。假设我有三个电话来清除可能抛出的资源。既然让异常离开析构函数是不好的,那么我的设计模式应该是什么?显然下面的方式是不可扩展的。

谢谢。

class B::~B(){

try{
   clearResourceA()
 }
 catch{
     try{
         clearResourceB();
        } 
     catch{
         clearResourceC();
     }
     clearResourceC();
 }
clearResourceB();
    .
    .
}

【问题讨论】:

  • 绝对不可扩展。试图只管理三个资源,你已经有逻辑错误。

标签: c++


【解决方案1】:

为什么不:

try{clearResourceA();} catch(...){}
try{clearResourceB();} catch(...){}
try{clearResourceC();} catch(...){}

【讨论】:

    【解决方案2】:

    将每个资源封装在一个类中,并在其析构函数中清除它们(使用周围的 try/catch):

    struct ProperlyManagedA {
        // some means of using the resource - a rudimentary way is this:
        A &getA() { return a; }
        const A &getA() const { return a; }
        // cleanup
        ~ProperlyManagedA() { 
            try {
                a.clear(); // whatever it is ClearResourceA actually does
            } catch (...) {}
        }
      private:
        A a;
    }
    

    带有自定义删除器的shared_ptr 是实现此目的的一种方法,无需为每种类型的资源创建整个类。

    您可能会改进丢弃异常(例如记录问题),具体取决于抛出的内容。

    更好的是,修改资源 A、B 和 C,以便它们在自己的析构函数中清除自己。不过,这可能是不可能的。

    无论哪种方式,您都可以将任意数量的此类资源放入一个类中,而无需向该类的析构函数添加任何代码。这就是“可扩展性”。 RAII 的全部意义在于,资源的每个用户不应该为了正确使用资源而编写清理代码。

    【讨论】:

    • 好吧。 RAII 的目的是清理全部在您的析构函数中完成,因此您无需调用 delete 或 freeHandle 或代码主体内的任何函数。这里的主要问题是清理代码通常不应该放在首位。
    • @CashCow:“清理都在你的析构函数中完成”——好吧,如果资源是按照 RAII 原则设计的,那么清理是在资源的析构函数中完成的。如果我必须自己编写 RAII 类,那么无可否认,那是我的析构函数。但是无论谁编写了 RAII 资源,该 RAII 资源的用户都不必编写或显式调用清理代码。这就是我所说的“用户”。
    • 谢谢,我在看其他建议时快疯了:/
    • 这是唯一惯用的答案。如果你写 C++ 而没有使用 RAII,那你就错了。
    【解决方案3】:

    使用 catch-all(即 catch (...))捕获任何可以在析构函数中抛出的东西,并尽最大努力处理抛出的异常。确保没有异常从析构函数中传播出去,这将帮助您防止这种情况发生。

    【讨论】:

      【解决方案4】:

      您还可以包装您的 ClearResources 函数以确保它们永远不会抛出。为什么他们还是会扔?

      【讨论】:

      • 某种刷新(或其他 I/O)是通常的原因。
      【解决方案5】:

      因为您询问了设计模式,所以我将告诉您我成功使用的经验法则。 所有具有清理/终止功能的函数都应该绝不抛出。

      这些函数通常是公认的名称:

      • 清除*()
      • 干净*()
      • 终止*()
      • 销毁*()
      • 释放*()
      • 分离*()
      • 免费*()
      • 擦除*()
      • ~析构函数()
      • ...

      这背后的基本原理是:

      • 每个资源必须至少有 1 个函数保证特定资源被清除
      • (递归)如果您构建清理函数并释放多个资源,则仅使用保证这些资源将以异常安全的方式释放的函数
      • 使用你的代码的程序员必须有办法清理资源
      • 如果您将清理函数导出到库之外,则不要传播异常(因为库的用户不会知道资源是否已释放)

      我可以尝试使用RAII pattern。但即使在这种情况下,上述规则也会被使用。

      【讨论】:

        【解决方案6】:
        try   
        {  
           ClearResourceA();  
        }  
        catch(...)  
        {  
           ExceptionWasThrown();  
        }  
        try   
        {  
           ClearResourceB();   
        }  
        catch(...)  
        {  
           ExceptionWasThrown();  
        }  
        ...
        

        【讨论】:

        • Offtop:如何强制缩进?
        • 使用“代码”按钮,而不是反引号。代码按钮上有一个 101010。输入您的代码(缩进),突出显示代码,然后按下代码按钮。
        • @Starkey:不幸的是,今天既没有这些按钮也没有预览。我问了元,但仍在等待答案
        • 四个初始空格表示“代码”,也不需要反引号。
        猜你喜欢
        • 2017-08-16
        • 2014-01-21
        • 2012-04-11
        • 1970-01-01
        • 2013-08-13
        • 2014-07-06
        • 2015-08-26
        • 2013-11-07
        相关资源
        最近更新 更多