为啥我的 Qt 程序在断言失败后继续运行?
Posted
技术标签:
【中文标题】为啥我的 Qt 程序在断言失败后继续运行?【英文标题】:Why does my Qt program keeps running after assertion failure?为什么我的 Qt 程序在断言失败后继续运行? 【发布时间】:2020-01-08 17:05:07 【问题描述】:我正在开发一个 Qt 应用程序,我对 Qt 对断言失败的行为感到非常困惑。确实,我注意到,虽然发生了断言失败,但我的程序仍然运行了一段时间......
考虑以下代码:
class MainWindow : public QMainWindow
Q_OBJECT
public:
MainWindow() : crash(false)
QWidget *widget = new QWidget;
openAct = new QAction(tr("&Open..."), this);
connect(openAct, &QAction::triggered, this, &MainWindow::open);
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAct);
private slots :
void open()
statusBar()->showMessage(tr("Invoked: File|Open"));
crash = true;
assert(false); // 1st assertion failure ; paintEvent() will be called once more anyway
private:
void paintEvent(QPaintEvent* evt) final
assert(!crash); // 2nd assertion failure ; execution stops here
QMainWindow::paintEvent(evt);
QMenu* fileMenu;
QAction* openAct;
bool crash;
;
在调试版本中运行此应用程序时,单击File->Open 将调用open()
,此时会发生断言失败。我希望程序执行在这里停止。但是,它没有。
确实,下一个绘制事件被正确处理并调用了函数paintEvent()
。 (我可以确定调用了paintEvent()
,因为函数中的断言失败会弹出第二个断言失败对话框。)
最后,我得到 2 个(模态)调试器对话框,显示 Abort/retry/Ignore。只有第二个对话框可用于交互。并单击此对话框上的 Abort 将调试器切换到与 paintEvent()
中的第二个 assert()
调用对应的代码行。
此时进入VS调试器显示如下调用栈:
... c++ runtime code ...
Example.exe!MainWindow::paintEvent(QPaintEvent * evt) Ligne 37 C++ -> 2nd assertion failure here...
... Qt code ...
[External code]
Example.exe!MainWindow::paintEvent(QPaintEvent * evt) Ligne 37 C++ -> ...or maybe here
... Qt code ...
[External code]
> Menus Example.exe!MainWindow::open() Ligne 31 C++ -> 1st assertion failure here
... Qt code ...
Example.exe!main(int argc, char * * argv) Ligne 10 C++
Example.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal) Ligne 111 C++
[External code]
我的问题是:
为什么我的应用程序在第一次断言失败时没有中止? 有什么方法可以确保应用程序在第一次断言失败发生时正确中止(与上述情况相同)?谢谢
【问题讨论】:
这是在 Release 还是 Debug 构建中? @drescherjm 我得到了 2 个调试器对话框:一个用于第一个断言,另一个用于第二个断言(两者都是带有 Abort/Retry/Ignore 按钮的标准调试器对话框) @drescherjm 是的,我的意思是 2 个模态对话框,并且只能与第 2 个交互,其中“Abort”按钮在第 2 个启动调试器assert()
可能与参加 pant 事件有关。
【参考方案1】:
assert(..)
语句只能用于检查调试版本中的条件。它永远不应该用于避免发布版本中的问题,因为断言不会在发布版本中执行。
在 Visual C++ 中,assert(..)
只为 DEBUG 构建生成代码。它在发布版本中什么也不做。来自文档of assert(..) on Microsoft's site:
只有在定义了 _DEBUG 时,断言语句才能编译。否则,编译器将断言视为空语句。因此,断言语句不会在您的最终发布程序中增加开销或性能成本,并允许您避免使用 #ifdef 指令。
Qt 还支持自己的跨平台断言版本:Q_ASSERT(..)
、Q_ASSERT_X(..)
和 Q_CHECK_PTR(..)
。与普通的 assert(..)
一样,这些也仅适用于 Debug 版本。
如果在编译期间定义了 QT_NO_DEBUG,Q_ASSERT()、Q_ASSERT_X() 和 Q_CHECK_PTR() 将展开为空。因此,这些宏的参数不应该有任何副作用。这是 Q_CHECK_PTR() 的错误用法:
就个人而言,如果我在 Qt 中进行开发,我会留在 Qt 中并且不会混用。因此,我更喜欢使用 Q_ASSERT。
【讨论】:
感谢 jwernerny,但我知道assert()
的用途。在这种情况下,我用它来调试我的应用程序,在这种情况下,它完美地完成了这项工作,向我展示了我的程序中的一个错误。我的问题是我希望我的程序在这种情况下中止。【参考方案2】:
为什么我的应用程序在第一次断言失败时没有中止?
因为你没有要求它。也许您想稍后设置断点并允许程序继续运行。也许你知道这个断言在这种特殊情况下是无害的,并希望忽略它。
在做出中止决定之前,不会尝试中止整个过程。调用assert
的线程只运行assert
代码,直到做出中止的决定。
有什么方法可以确保应用程序在第一次断言失败发生时正确中止(在与上述相同的情况下)?
如果assert
没有完全按照您的意愿执行,请创建您自己的宏来执行您想要执行的任何操作。如果您想立即强制中止该过程,您可以这样做。
【讨论】:
断言失败,所以我希望会弹出一个调试器对话框并询问我是否要中止;这至少应该暂停执行。这不会发生,并且会继续执行,直到发生第二次断言失败,此时会弹出 2 个预期的对话框。 @shrike 它确实会暂停调用线程的执行,但不会暂停整个进程。暂停整个过程并不是真正的事情。 应用程序是单线程的。调用堆栈清楚地表明了这一点:assert()
都发生在同一个调用堆栈中。如果你有安装了Qt sdk的windows机器,请自己试试我的代码,你会看到的。以上是关于为啥我的 Qt 程序在断言失败后继续运行?的主要内容,如果未能解决你的问题,请参考以下文章
调试断言失败:_CrtIsValidHeapPointer(pUserData)