【问题标题】:Exception Handling in Qt for Class HierarchyQt 中用于类层次结构的异常处理
【发布时间】:2013-12-25 14:42:52
【问题描述】:

我正在为 Windows 平台开发一个 Qt 应用程序。在类层次结构中使用异常处理时遇到问题。

我在 A 类的函数中实例化了 B 类对象。当由于某种原因从 B 类抛出异常(并且没有在 B 类中捕获)时,它没有在 A 类中被捕获(适当的尝试- catch 块存在于 A 类中),而是应用程序崩溃显示一些特定于 Windows 的错误。类层次结构中的这种类型的 try-catch 机制在 Java 中运行良好。

例子:

这是一段 ClassA 的代码,它正在实例化 ClassB 的对象(Qt 对话框)

void Class A::on_pbCallB_clicked()
{
    try
    {
        objClassB = new ClassB();
        objClassB->show();
    }
    catch(QString *strExceptionMsg)
    {
        QMessageBox::critical(this,"Error",*strExceptionMsg);
        exit(1);
    }
    catch(...)
    {
        QMessageBox::critical(this,"Error","Uknown Error");
        exit(1);
    }
}

当 ClassB 对话框显示并按下对话框上的按钮时,调用以下代码:

void ClassB::on_pbThrowExp_clicked()
{
    try
    {
        throw (new QString("Throwing Exception !"));
    }
    catch(QString *strExceptionMsg)
    {
        throw strExceptionMsg;
    }
    catch(...)
    {
        throw (new QString("Unknown Error"));
    }
}

这会引发一个异常,该异常在 ClassB 的函数中被捕获,但当进一步抛出时,它不会在 ClassA 中被捕获(从 objClassB 实例化的位置)并且整个应用程序崩溃。

我尝试了 1 个解决方案,其中我重新实现了 QApplication 的 notify 方法,其中,从应用程序某处抛出的异常,如果没有在任何地方捕获,则在重新实现的 notify 方法中被捕获。但是这样做并不能阻止应用程序关闭。

请确认这在 Qt/C++ 中是否可行,如果不可行,请指出 Qt 中的替代方案(如果可用)。

【问题讨论】:

  • 请提供SSCCE。仅通过代码描述很难准确判断发生了什么。
  • 感谢您的 cmets。我已经添加了示例代码和一些我已经尝试过的信息。希望这会使问题更清楚。
  • 您正在泄漏 QStrings - 不要在堆上创建它们。使用throw QString(...)catch(const QString& ...)。除此之外:如果从 A 调用 on_pbThrowExp_clicked(),您的代码将起作用。但事实并非如此,它是由按钮单击的事件处理代码调用的。离开槽的异常只能在 notify() 中捕获,因此意义不大 - 最佳实践是确保在槽中捕获所有异常。
  • @Frank ..感谢有关泄漏 QStrings 的信息......关于异常,我想除了在插槽中捕获它们之外我别无选择。

标签: c++ qt exception-handling


【解决方案1】:

这是一个老问题,但由于仍然可能有人试图将 Qt 与异常处理结合起来,以下是一个对我有用的解决方案。不过它需要 c++11 支持。

应用程序.hpp:

#ifndef APPLICATION_HPP
#define APPLICATION_HPP

#include <QApplication>
#include <exception>

///
/// This class catches any exceptions thrown inside proc()
/// and shows them using the message() function.
/// The virtual message() function can be overriden to customize
/// how the messages are shown. The default implementation
/// shows them using QMessageBox.
///
class Application: public QApplication
{
public:
    Application(int& argc, char* argv[]);
    bool notify(QObject* receiver, QEvent* event);

    virtual int proc() { return exec(); }
    int run();

    virtual int message(const std::string&);

private:
    std::exception_ptr _M_e = nullptr;
};

#endif // APPLICATION_HPP

应用程序.cpp

#include "application.hpp"
#include <QMessageBox>

Application::Application(int& argc, char* argv[]):
    QApplication(argc, argv)
{ }

int Application::run()
{
    try
    {
        int code = proc();
        // Check if an exception was thrown and stored in Application::notify
        // and if so, rethrow it.
        if(_M_e) std::rethrow_exception(_M_e);

        return code;
    }
    catch(std::exception& e)
    {
        return message(e.what());
    }
}

int Application::message(const std::string& message)
{
    QMessageBox::critical(0, "Error", QString::fromStdString(message));
    return 1;
}

///
/// Qt does not allow exceptions thrown from event handlers
/// to be processed outside the event loop.
/// So we catch them here, store them in _M_e
/// and tell the application to exit.
///
bool Application::notify(QObject* receiver, QEvent* event)
{
    try
    {
        return QApplication::notify(receiver, event);
    }
    catch(...)
    {
        _M_e = std::current_exception();
        exit();
    }
    return false;
}

您可以像使用QApplication 一样使用Application 类,除了调用run 函数而不是exec。 (或者,将run 重命名为exec 以隐藏QApplicationexec。)

您可以覆盖message 函数来自定义错误消息。您还可以覆盖proc 函数,在exec 之前/之后添加一些初始化/销毁代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    相关资源
    最近更新 更多