1

在我正在编写的应用程序中,我在大部分错误处理中都使用了异常。我还没有定义我自己的异常类,我只是做了以下事情:

namespace Mage {
    typedef std::exception Exception;
}

这样,当我稍后定义自己的类型时,我就不必更改所有代码,该类型应该使用相同的接口。

也就是说,任何异常都会使我的应用程序崩溃。考虑到上述定义,为什么会崩溃?

void Mage::Root::initialize(Mage::String& p_log) {
    // initialize GLFW and GLEW.
    if (!glfwInit()) {
        throw new Mage::Exception("failed to initialize OpenGL");
        return;
    } else m_GLFWInitialized = true;

无论我删除还是保留“新”,它仍然会崩溃。我错过了什么吗?我查找了教程,但那些并没有让我更聪明。

我也在这里发现了错误:

try {
    MAGE_ROOT.initialize(Mage::String("Mage.log"));
} catch (Mage::Exception& e) {
    std::cerr << e.what() << std::endl;
}

我遇到的崩溃是:

Debug Error!

Program: ...sual Studio 2010\Project\Mage3D\Binaries\Debug\Test.exe

R6010
- abort() has been called

(Press Retry to debug application)
4

3 回答 3

11

问题是你没有捕捉到你的异常。

我不知道你 - 必须 - 捕捉异常(来自评论

是的,你必须。如果没有捕捉到抛出的异常,std::terminate()将被调用。这是预期的行为:存在异常是为了防止程序员忘记错误处理。

这就是说,我建议:

  • 按价值投掷;
  • 通过引用捕捉

例如:

void foo()
{
    // ...
    throw std::logic_error("Error!");
    //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //    Throw by value (std::logic_error derives from std::exception)
    // ...
}

void bar()
{
    try
    {
        // ...
        foo();
        // ...
    }
    catch (std::exception& e)
           ^^^^^^^^^^^^^^^
    //     Catch by reference
    {
        std::cout << e.what(); // For instance...
    }
}

更新:

关于您发布的那段代码,您正在抛出一个指针并通过引用捕获。处理程序将不匹配。并且由于没有其他匹配的处理程序,std::terminate()将被调用。

相反,您应该按值抛出异常:

throw Mage::Exception("failed to initialize OpenGL");

如果您发布的代码确实是您正在使用的代码,您将看到控制权已转移到您的处理程序。

于 2013-04-26T12:50:03.087 回答
2

根据错误消息,您正在为您的项目使用 Visual Studio (2010)。除非你将你的 throw 包裹在一个 try/catch 块中,否则它将“飞过屋顶”并由 C++ 运行时“处理”,这意味着调用 abort()。您可能希望在调用堆栈中像这样更高:

try
{
   SomeFunctionThatUltimatelyThrows();
}
catch(Exception & e)
{
   // .. handle error - log, resume, exit, whatever
}

另请注意 Scott Meyers 建议始终通过引用捕获异常。“异常”:如果您使用的是 MFC CExceptions,您希望通过指针捕获并调用 Delete 方法以实现基于堆的自毁异常。

根据您的编辑,您可能在“按指针”抛出和“按引用”捕获之间存在不匹配。如果您已经解决了这个问题并且仍然没有让您的 catch 块执行,您可以尝试使用 CRT SetAbortHandler 来调试 abort() 调用来安装您自己的 abort 函数。这可以简单地链接到现有的,但可以设置断点并检查调用堆栈以查看发生了什么问题。

于 2013-04-26T12:49:46.537 回答
1

傻瓜的 C++ try-catch-throw 逻辑。请注意,这不包括 RAII/基于堆栈的分配/销毁。

  • 当您抛出异常时,异常被称为“传播”。它向上传播调用堆栈,直到找到第一个可以处理它的处理程序(因此它被捕获)或直到它到达调用堆栈的根。
    • 如果它被捕获,则从捕获异常的点继续执行。异常在 catch 块的末尾被破坏。
    • 如果它找到根,它调用std::unhandled_exception,它通常调用std::terminate,它通常调用abort()。简而言之,一切都尽快下降。
  • 如果在当前传播异常时抛出异常,则一次将有两个传播。Java 和 C# 有处理这个问题的巧妙方法,但是这不应该首先发生 - 没有异常处理程序可以在逻辑上处理异常组合。不要在当前传播时抛出异常。即使您不使用不应使用的 std::uncaught_exception() ,这条规则也不难遵守。
  • 在展开堆栈/传播异常时,在堆栈上找到的所有对象都被破坏。这些析构函数永远不应该抛出异常——毕竟,当销毁一个对象“失败”时,在析构函数修复它之后你还要做什么?
  • 总是按值抛出,按引用捕获。如果你通过指针 throw & catch,你很可能会泄漏一些东西,这是通过引用是不可能的。如果您按值捕获,您将切掉派生的异常类型。通过引用捕捉。
  • 在您的软件的根目录中,包含一个包罗万象的 - catch(...). 这并不能让你找出你到底抓到了什么,但至少你可以安全地坠毁。当被调用的代码可能抛出你不知道的“东西”时,也要这样做。
于 2013-04-26T14:02:54.390 回答