【发布时间】:2019-06-09 11:45:11
【问题描述】:
我有一个使用 Qt 的 C++ 程序,其 GUI QObject 有一个与函数 (bar()) 链接的 ComboBox(我们称之为 foo)。这个 QObject 被称为对象A。
组合框在构造函数中链接:
connect(foo, SIGNAL(currentIndexChanged(int)), SLOT(bar(int)));
在bar(int)中,这个函数在函数结束时被调用:
inspector->update();
在检查器的update 函数中,会发生这种情况:
A被删除(delete A;,几乎是 - 它是用new创建的)。创建了一个与
A相同类型的新实例,我们称之为对象B。
这被初始化并运行了一会儿,然后一切都变得非常糟糕。
程序段错误,但只是由于 Qt 错误。 GDB 没有多大帮助,但通过 Valgrind memcheck 运行程序会产生大量无效读取,如下所示:
==23410== Invalid read of size 8
==23410== at 0xFE92D7A: QMetaObject::activate(QObject*, int, int, void**) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.9.5)
==23410== by 0x79DDC94: QComboBox::currentIndexChanged(QString const&) (in /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5.9.5)
==23410== by 0x79DFB2D: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5.9.5)
...
==23410== Address 0x3218ca68 is 8 bytes inside a block of size 48 free'd
==23410== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23410== by 0xFE9119A: QObjectPrivate::deleteChildren() (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.9.5)
==23410== by 0x7913D4B: QWidget::~QWidget() (in /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5.9.5)
在我看来,来自foo 的信号正在再次发送,并试图调用不存在的对象A 的bar()。但这怎么可能?当然delete A 也应该阻止任何信号通过?另外,我只更改组合框的值一次 - 为什么要发送多个信号?
我试过屏蔽信号,也试过重新安排,但没有成功。
这是完整的相关Valgrind memcheck log。
这是 Qt 的错误吗?还是我做错了什么?非常感谢任何帮助,包括进一步调试的帮助。
【问题讨论】:
-
你真的应该提供一个minimal reproducible example。但是请注意,在
QObject *上调用delete通常是个坏主意。而不是delete A,你应该使用A->deleteLater()。 -
您应该停止使用旧的
SIGNAL和SLOT宏,它们只在运行时检测连接错误(并且速度很慢,因为它们基于字符串比较)。而是采用基于成员函数指针的新的、经过编译时检查的语法。在很多情况下,这也可以让您避免代码膨胀Q_OBJECT宏。 -
看起来您删除了由来自 A 的信号调用的代码中的 A。然后在发出信号的地方继续执行,对已删除的
this进行操作。 (另见stackoverflow.com/questions/4888189/…)。我会考虑使用 deleteLater(),或者,可能更好的是,一个 QueuedConnection 用于信号/插槽连接。 -
通用汽车“但是请注意,在 QObject * 上调用 delete 通常是一个坏主意”。我不同意,删除通常很好,默认情况下使用 deleteLater() 而不考虑使代码变得更不可预测,并且可能隐藏问题并创建新问题(例如,如果对象拥有资源或继续做某事,则在 deleteLater 之间() 和实际删除)。
-
deleteLater 不能解决问题。我会看看我是否可以组合一个最小的可重现示例,但又是 C++ 和 Qt,所以“最小”并不意味着太多......
标签: c++ qt segmentation-fault valgrind