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

使用STD::析构函数中的unCuTtJug异常

  •  8
  • anon  · 技术社区  · 14 年前

    有些文章总结出“从不从析构函数中抛出异常”,“STD::unCuctUnExtExter())是无效的,例如:

    但我似乎没有明白这一点。所以我写了一个小的测试示例(见下文)。

    由于测试示例的一切都很好,我非常感谢对它可能有什么问题的一些评论?

    测试结果:

    /主

        Foo::~Foo(): caught exception - but have pending exception - ignoring
        int main(int, char**): caught exception: from int Foo::bar(int)
    

    /主1

        Foo::~Foo(): caught exception -  but *no* exception is pending - rethrowing
        int main(int, char**): caught exception: from Foo::~Foo()
    

    例子:

    // file main.cpp
    // build with e.g. "make main"
    // tested successfully on Ubuntu-Karmic with g++ v4.4.1
    #include <iostream>
    
    class Foo {
      public:
    
      int bar(int i) {
        if (0 == i)
          throw(std::string("from ") + __PRETTY_FUNCTION__);
        else
          return i+1;
      }
    
      ~Foo() {
        bool exc_pending=std::uncaught_exception();
        try {
          bar(0);
        } catch (const std::string &e) {
          // ensure that no new exception has been created in the meantime
          if (std::uncaught_exception()) exc_pending = true;
    
          if (exc_pending) {
            std::cerr << __PRETTY_FUNCTION__ 
                      << ": caught exception - but have pending exception - ignoring"
                      << std::endl;
          } else {
            std::cerr << __PRETTY_FUNCTION__
                      << ": caught exception -  but *no* exception is pending - rethrowing"
                      << std::endl;
            throw(std::string("from ") + __PRETTY_FUNCTION__);
          }
        }
      }
    
    };
    
    int main(int argc, char** argv) {
      try {
        Foo f;
        // will throw an exception in Foo::bar() if no arguments given. Otherwise
        // an exception from Foo::~Foo() is thrown.
        f.bar(argc-1);
      } catch (const std::string &e) {
        std::cerr << __PRETTY_FUNCTION__ << ": caught exception: " << e << std::endl;
      }
      return 0;
    }
    

    补充 :换句话说:尽管有些文章中有警告,但它还是按预期工作-那么它可能有什么问题?

    3 回复  |  直到 6 年前
        1
  •  6
  •   Yakov Galka    9 年前

    你的代码在技术上没有问题。这是完全安全的,因为你不会意外地终止,因为你在不安全的时候抛出了一个异常。问题是它也没有用处,因为它偶尔也不会在安全的时候抛出异常。析构函数的文档基本上必须说“这可能引发异常,也可能不会引发异常。”

    如果它偶尔不会抛出异常, 你最好不要破例 . 这样,你至少是始终如一的。

        2
  •  8
  •   R Samuel Klatchko    14 年前

    Herb Sutter指的是另一个问题。他说的是:

    try
    {
    }
    catch (...)
    {
        try
        {
            // here, std::uncaught_exception() will return true
            // but it is still safe to throw an exception because
            // we have opened a new try block
        }
        catch (...)
        {
        }
    }
    

    所以问题是如果 std::uncaught_exception() 返回true,您不确定是否可以安全地抛出异常。你最终不得不避免在 STD:: 为了安全返回真值。

        3
  •  0
  •   proski    6 年前

    赫伯·萨特正在谈论一个阶级对象 T 在类的对象中存在未捕获的异常时被销毁 U . std::uncaught_exception() 会回来的 true T 析构函数。析构函数将无法确定是否在堆栈展开期间调用它。如果是,就不能扔,否则就照常做。

    班级 U 使用类会有问题 T 在析构函数中。 U 会发现自己在处理一个无用的 T 对象将拒绝在其析构函数中执行任何有风险的操作(可能包括写入日志文件或向数据库提交事务)。

    赫伯·萨特建议永远不要扔破坏者,这是个好主意。然而,C++ 17提供了另一种选择。它介绍了 std::uncaught_exceptions() 可以用来判断析构函数是否可以抛出。下面的示例显示了如果在C++ 14模式中执行的问题。如果用C++ 17模式编译,它会正常工作。

    
    #include <exception>
    #include <iostream>
    #include <string>
    
    class T
    {
      public:
    
        ~T() noexcept(false)
        {
    #if __cplusplus >= 201703L
          // C++17 - correct check
          if (std::uncaught_exceptions() == uncaught_exceptions_)
    #else
          // Older C++ - incorrect check
          if (!std::uncaught_exception())
    #endif
          {
            throw (std::string{__PRETTY_FUNCTION__} + " doing real work");
          }
          else
          {
            std::cerr << __PRETTY_FUNCTION__ << " cowardly quitting\n";
          }
        }
    
      private:
    
    #if __cplusplus >= 201703L
        const int uncaught_exceptions_ {std::uncaught_exceptions()};
    #endif
    };
    
    class U
    {
      public:
    
        ~U()
        {
          try
          {
            T t;
          }
          catch (const std::string &e)
          {
            std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
          }
        }
    };
    
    int main()
    {
      try
      {
        U u;
        throw (std::string{__PRETTY_FUNCTION__} + " threw an exception");
      }
      catch (const std::string &e)
      {
        std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
      }
      return 0;
    }