问:如何处理源自Qt类内部的崩溃?

时间:2014-07-18 17:38:12

标签: c++ qt debugging qtcore qlist

我正在做一些关于在基于Qt的应用程序中处理错误的研究,在做了关于Qt使用断言而不是异常抛出的一些阅读后,我得出了以下问题:

Qt类的某些方法会使应用程序在发生错误时立即崩溃。例如,如果我使用Qt容器(例如QList)并且要求成员大于容器的大小,则软件会立即关闭:

QList<int> myList;
int itemp = myList.at(1);

//Crash!

现在我不希望在我的应用程序中发生这种情况:如果发生这样的事件,我想处理该事件(即使只是在错误日志中记录某些内容并稍后关闭该应用程序)。但是,如果Qt在我可以进行任何错误检查之前崩溃软件,我怎么能这样做呢?并且将代码置于try-catch中是行不通的,因为Qt在发生这种情况时不会抛出任何东西(至少在没有启用异常的时候,这是我假装使用的)。有办法解决这个问题吗?

1 个答案:

答案 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;

话虽如此,记录此错误的想法值得怀疑。你应该避免出现这个错误而不是事后处理它。