我正在做一些关于在基于Qt的应用程序中处理错误的研究,在做了关于Qt使用断言而不是异常抛出的一些阅读后,我得出了以下问题:
Qt类的某些方法会使应用程序在发生错误时立即崩溃。例如,如果我使用Qt容器(例如QList)并且要求成员大于容器的大小,则软件会立即关闭:
QList<int> myList;
int itemp = myList.at(1);
//Crash!
现在我不希望在我的应用程序中发生这种情况:如果发生这样的事件,我想处理该事件(即使只是在错误日志中记录某些内容并稍后关闭该应用程序)。但是,如果Qt在我可以进行任何错误检查之前崩溃软件,我怎么能这样做呢?并且将代码置于try-catch中是行不通的,因为Qt在发生这种情况时不会抛出任何东西(至少在没有启用异常的时候,这是我假装使用的)。有办法解决这个问题吗?
答案 0 :(得分:4)
这不是真正的崩溃。这在Linux等上称为SIGABRT。您所看到的基本上是Q_ASSERT_X的结果:
template <typename T>
inline const T &QList<T>::at(int i) const
{ Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::at", "index out of range");
return reinterpret_cast<Node *>(p.at(i))->t(); }
这不是Qt特定的,只是通用的C ++,所以处理它的唯一方法本质上是特定于平台的。例如,在带有POSIX的Linux上,您将编写一个信号处理程序,您可以注册该信号。例如,您可以查找man signal(7)
。见这句话:
处理SIGABRT
处理此信号时,应记住abort(3)函数的工作原理:它将信号上升两次,但第二次SIGABRT处理程序恢复到默认状态,因此即使你有一个,程序也会终止处理程序定义因此,在程序终止之前,您实际上有机会在中止(3)的情况下执行某些操作。如前所述,不通过退出信号处理程序并使用longjmp(3)来终止程序是不可能的。
所以,这是你基本上可以做的:
jmp_buf env;
void sigabrt_handler(int signum)
{
longjmp (env, 1);
}
void handle_func(void (*func)(void))
{
if (setjmp (env) == 0) {
signal(SIGABRT, &sigabrt_handler);
(*func)();
}
else {
qDebug() << "Aborted\n";
}
}
然后你会用你的函数调用这个处理程序:
handle_func(my_func_void_retval_void_args);
同样,您需要在要使用的所有平台上实现此功能。
你也可以忽略它在这里满足宏条件,即QT_NO_DEBUG
定义和QT_FORCE_ASSERT
未定义。后者应该是默认值。
#if !defined(Q_ASSERT_X)
# if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
# define Q_ASSERT_X(cond, where, what) qt_noop()
# else
# define Q_ASSERT_X(cond, where, what) ((!(cond)) ? qt_assert_x(where, what,__FILE__,__LINE__) : qt_noop())
# endif
#endif
有关详细信息,请参阅其文档:
Q_ASSERT_X对于在开发期间测试前后条件非常有用。如果在编译期间定义了\ c QT_NO_DEBUG,它什么都不做。
另一种方法是在实际索引之前检查the size()。那会让你免于崩溃。让我举个例子:
QList<int> myList;
int itemp = myList.size() ? myList.at(1) : 0;
话虽如此,记录此错误的想法值得怀疑。你应该避免出现这个错误而不是事后处理它。