代码之家  ›  专栏  ›  技术社区  ›  WilliamKF

当抛出特定异常时C++析构函数跳过工作的方法?

  •  1
  • WilliamKF  · 技术社区  · 14 年前

    我在堆栈上有一个对象,我希望它的析构函数在调用析构函数时跳过一些工作,因为堆栈由于在堆栈上对象的作用域中抛出了一个特定的异常而被解绕。

    现在,我可以在堆栈项的范围内添加一个try catch块,并捕获相关的异常,并通知堆栈对象不要运行要跳过的工作,然后按如下方式重新引发异常:

    RAII_Class pending;
    
    try {
      doSomeWorkThatMayThrowException();
    } catch (exceptionToSkipPendingDtor &err) {
      pending.notifySkipResourceRelease();
      throw;
    }
    

    不过,我希望有一种更优雅的方式来做到这一点。例如,想象一下:

    RAII_Class::~RAII_Class {
      if (detectExceptionToSkipPendingDtorBeingThrown()) {
        return;
      }
      releaseResource();
    }
    
    7 回复  |  直到 14 年前
        1
  •  8
  •   CB Bailey    14 年前

    你几乎可以用 std::uncaught_exception() 但不完全是。

    Herb Sutter比我解释的“几乎”更好: http://www.gotw.ca/gotw/047.htm

    有一些角落的箱子 std::未捕获的_异常() 从析构函数调用时返回true,但相关对象实际上未被堆栈展开过程破坏。

    没有RAII你可能会更好,因为它与你的用例不匹配。RAII是指始终清理;是否例外。

    您想要的要简单得多:只有在不抛出异常(这是一个简单的函数序列)时才释放资源。

    explicitAllocateResource();
    doSomeWorkThatMayThrowException();
    explicitReleaseResource(); // skipped if an exception is thrown
                               // by the previous function.
    
        2
  •  5
  •   Georg Fritzsche    14 年前

    我会用另一种方式来做——如果没有异常,明确地告诉它做它的工作:

    RAII_Class pending;
    
    doSomeWorkThatMayThrowException();
    
    pending.commit(); // do or prepare actual work
    
        3
  •  3
  •   SoapBox    14 年前

    这似乎规避了使用RAII的主要原因。RAII的要点是,如果在代码中间发生异常,您仍然可以释放资源/进行适当的销毁。

    如果这不是你想要的语义,那么不要使用raii。

    因此,而不是:

    void myFunction() {
        WrapperClass wc(acquireResource());
    
        // code that may throw
    }
    

    只做:

    void myFunction() {
        Resource r = acquireResource();
    
        // code that may throw
    
        freeResource(r);
    }
    

    如果中间的代码抛出,则不会释放资源。这是您想要的,而不是保留RAII(和保留名称),但不实现RAII语义。

        4
  •  0
  •   WilliamKF    14 年前

    看起来像bool std::uncaught_Exception();如果您希望对每个异常都使用这种行为,而不仅仅是特殊的行为,那么就可以使用这个技巧!

        5
  •  0
  •   wilhelmtell    14 年前

    您可以不使用尝试捕获:

    RAII_Class pending;
    doSomeWorkThatMayThrowException();  // intentional: don't release if throw
    pending.releaseResource();
    

    或者,你也可以用RAII更用力一点:

    struct RAII_Class {
        template<class Op>
        void execute(Op op) {
            op();
            releaseResources();
        }
    
    private:
        void releaseResources() { /* ... */ }
    };
    
    int main(int argc, char* argv[])
    {
        RAII_Class().execute(doSomeWorkThatMayThrowException);
        return 0;
    }
    
        6
  •  0
  •   Drew Hall    14 年前

    虽然这充其量只是一个拼凑,但如果您拥有感兴趣的异常类的代码,您可以向该类(bool)添加一个静态数据成员,该成员在该类的对象的构造函数中设置为“true”,在析构函数中设置为“false”(可能需要改为递增/递减的int)。然后在raii类的析构函数中,可以检查std::uncaught_Exception(),如果为true,则查询异常类中的静态数据成员。如果返回true(或gt;0),则会得到其中一个异常——否则将忽略它。

    不是很优雅,但它可能会起作用(只要你没有多个线程)。

        7
  •  0
  •   Scott Turley    14 年前

    我发现这个网站上有一个关于std::uncaught_Exception()的有趣讨论,还有一个对你的问题的替代解决方案,对我来说更优雅、更正确:

    http://www.gotw.ca/gotw/047.htm

    //  Alternative right solution
    //
    T::Close() {
      // ... code that could throw ...
    }
    
    T::~T() /* throw() */ {
      try {
        Close();
      } catch( ... ) {
      }
    }
    

    这样,析构函数只做一件事,并且可以防止在异常期间抛出异常(我假设这是您试图解决的问题)。