抛出异常时应用程序崩溃

Application crashes when throwing an exception

本文关键字:崩溃 应用程序 抛出异常      更新时间:2023-10-16

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

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;

无论我删除或保留'new',它仍然会崩溃。我错过什么了吗?我已经查了教程,但那些并没有让我更聪明。

我也在这里捕获了错误:

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

我得到的崩溃是:

Debug Error!
Program: ...sual Studio 2010ProjectMage3DBinariesDebugTest.exe
R6010
- abort() has been called
(Press Retry to debug application)

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

我不知道你必须从注释中捕获异常

是的,你必须。如果没有捕获抛出的异常,则会调用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");

如果你发布的代码确实是你正在使用的,你会看到控制被转移到你的处理程序。

根据错误信息,您正在为您的项目使用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()调用。这可以简单地链接到现有的,但将提供一个机会来设置一个断点,并检查调用堆栈,看看哪里出了问题。

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

    当你抛出一个异常时,这个异常被称为"正在传播"。它沿着调用堆栈向上传播,直到找到可以处理它的第一个处理程序(因此它被捕获),或者直到它到达调用堆栈的根。
    • 如果被捕获,则从捕获异常的点开始继续执行。异常在catch块的末尾被销毁。
    • 如果它找到根,它调用std::unhandled_exception,它通常调用std::terminate,后者通常调用abort()。简而言之,一切都要尽快放下。
  • 如果在异常正在传播时抛出异常,则会同时有两个异常在传播。Java和c#都有处理这种情况的可爱方法,但这种情况从一开始就不应该发生——没有异常处理程序在逻辑上处理异常的组合。当一个异常正在传播时不要抛出异常。即使你不使用std::uncaught_exception(),这个规则也不难遵守。
  • 当展开堆栈/传播异常时,在堆栈上找到的所有对象都被销毁。这些析构函数永远不应该抛出异常——毕竟,当销毁对象"失败"时,在析构函数之后还能做些什么来修复它呢?
  • 总是按值抛出,按引用捕获。如果你扔&通过指针捕获,你很可能会泄漏一些东西,这是不可能的引用。如果按值捕获,则将剥离派生的异常类型。
  • 在您的软件的根,包括一个捕获所有- catch(...)。这不能让你知道你到底抓住了什么,但至少你可以安全坠毁。当被调用的代码可能抛出你不知道的"某些东西"时,也要这样做。