27

是否有某种方法可以捕获未处理的异常(包括那些在 catch 块之外抛出的异常)?

我并不真正关心使用异常完成的所有正常清理工作,只是我可以捕获它,将其写入日志/通知用户并退出程序,因为这些情况下的异常通常是致命的、不可恢复的错误。

就像是:

global_catch()
{
    MessageBox(NULL,L"Fatal Error", L"A fatal error has occured. Sorry for any inconvience", MB_ICONERROR);
    exit(-1);
}
global_catch(Exception *except)
{
    MessageBox(NULL,L"Fatal Error", except->ToString(), MB_ICONERROR);
    exit(-1);
}
4

8 回答 8

27

这可用于捕获意外异常。

catch (...)
{
    std::cout << "OMG! an unexpected exception has been caught" << std::endl;
}

如果没有 try catch 块,我认为您无法捕获异常,因此请构建您的程序,以便异常代码在 try/catch 的控制下。

于 2008-11-09T16:54:24.293 回答
26

查看std::set_terminate()

编辑:这是一个带有异常匹配的完整示例:

#include <iostream>
#include <exception>
#include <stdexcept>

struct FooException: std::runtime_error {
    FooException(const std::string& what): std::runtime_error(what) {}
};

int main() {
    std::set_terminate([]() {
        try {
            std::rethrow_exception(std::current_exception());
        } catch (const FooException& e) {
            std::cerr << "Unhandled FooException: " << e.what() << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Unhandled exception: " << e.what() << std::endl;
        } catch (...) {
            std::cerr << "Unhandled exception of unknown type" << std::endl;
        }

        std::abort();
    });

    throw FooException("Bad things have happened.");
    // throw std::runtime_error("Bad things have happened.");
    // throw 9001;
}
于 2012-03-08T13:51:59.403 回答
12

您可以在 Windows 上使用SetUnhandledExceptionFilter,它将捕获所有未处理的 SEH 异常。

通常,这足以解决您的所有问题,因为 IIRC 所有 C++ 异常都是作为 SEH 实现的。

于 2008-11-09T17:24:24.327 回答
9

没有任何 catch 块,您将不会捕获任何异常。您可以在 main() 中有一个 catch(...) 块(以及每个附加线程中的等价物)。在这个 catch 块中,您可以恢复异常详细信息,并且可以对它们执行一些操作,例如记录和退出。

但是,一般的 catch(...) 块也有缺点:系统发现异常已由您处理,因此不再提供任何帮助。在 Unix/Linux 上,此帮助将构成创建一个 CORE 文件,您可以将其加载到调试器中并查看未例外异常的原始位置。如果您使用 catch(...) 处理它,则此信息已经丢失。

在 Windows 上,没有 CORE 文件,所以我建议使用 catch(...) 块。从该块中,您通常会调用一个函数来恢复实际的异常:

std::string ResurrectException()
   try {
       throw;
   } catch (const std::exception& e) {
       return e.what();
   } catch (your_custom_exception_type& e) {
       return e.ToString();
   } catch(...) {
       return "Ünknown exception!";
   }
}


int main() {
   try {
       // your code here
   } catch(...) {
       std::string message = ResurrectException();
       std::cerr << "Fatal exception: " << message << "\n";
   }
}
于 2008-11-09T18:34:49.333 回答
7

更新:这仅涵盖 c++98。

从Meyers 的《更有效的 C++》(第 76 页)中,您可以定义一个函数,当函数生成其异常规范未定义的异常时调用该函数。

void convertUnexpected()
{
    // You could redefine the exception here into a known exception
    // throw UnexpectedException();

    // ... or I suppose you could log an error and exit.
}

在您的应用程序中注册功能:

std::set_unexpected( convertUnexpected );

如果函数生成未由其异常规范定义的异常,您的函数 convertUnexpected() 将被调用......这意味着这仅在您使用异常规范时才有效。;(

于 2008-11-09T18:44:54.290 回答
6

这就是我在 main() 中经常做的事情

int main()
{
    try
    {
        // Do Work
    }
    catch(std::exception const& e)
    {
         Log(e.what());
         // If you are feeling mad (not in main) you could rethrow! 
    }
    catch(...)
    {
         Log("UNKNOWN EXCEPTION");
         // If you are feeling mad (not in main) you could rethrow! 
    }
}
于 2008-11-09T17:11:14.337 回答
5

如果 C++11 可用,则可以使用此方法(参见示例:http ://en.cppreference.com/w/cpp/error/rethrow_exception ):

#include <iostream>
#include <exception>

void onterminate() {
  try {
    auto unknown = std::current_exception();
    if (unknown) {
      std::rethrow_exception(unknown);
    } else {
      std::cerr << "normal termination" << std::endl;
    }
  } catch (const std::exception& e) { // for proper `std::` exceptions
    std::cerr << "unexpected exception: " << e.what() << std::endl;
  } catch (...) { // last resort for things like `throw 1;`
    std::cerr << "unknown exception" << std::endl;
  }
}

int main () {
  std::set_terminate(onterminate); // set custom terminate handler
  // code which may throw...
  return 0;
}

这种方法还允许您为未处理的异常自定义控制台输出:拥有类似这样的东西

unexpected exception: wrong input parameters
Aborted

而不是这个:

terminate called after throwing an instance of 'std::logic_error'
  what():  wrong input parameters
Aborted
于 2016-06-21T14:33:59.687 回答
1

在所有异常屏障(不仅仅是主线程)中使用 catch (...)。我建议您始终重新抛出 (...) 并将标准输出/错误重定向到日志文件,因为您无法在 (...) 上执行有意义的 RTTI。OTOH,像 GCC 这样的编译器将输出关于未处理异常的相当详细的描述:类型、what() 的值等。

于 2008-11-09T22:35:13.060 回答