【问题标题】:Qt signal still firing after QObject deletedQObject 删除后 Qt 信号仍然触发
【发布时间】: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 的信号正在再次发送,并试图调用不存在的对象Abar()。但这怎么可能?当然delete A 也应该阻止任何信号通过?另外,我只更改组合框的值一次 - 为什么要发送多个信号?

我试过屏蔽信号,也试过重新安排,但没有成功。

这是完整的相关Valgrind memcheck log

这是 Qt 的错误吗?还是我做错了什么?非常感谢任何帮助,包括进一步调试的帮助。

【问题讨论】:

  • 你真的应该提供一个minimal reproducible example。但是请注意,在QObject * 上调用delete 通常是个坏主意。而不是delete A,你应该使用A->deleteLater()
  • 您应该停止使用旧的SIGNALSLOT 宏,它们只在运行时检测连接错误(并且速度很慢,因为它们基于字符串比较)。而是采用基于成员函数指针的新的、经过编译时检查的语法。在很多情况下,这也可以让您避免代码膨胀 Q_OBJECT 宏。
  • 看起来您删除了由来自 A 的信号调用的代码中的 A。然后在发出信号的地方继续执行,对已删除的 this 进行操作。 (另见stackoverflow.com/questions/4888189/…)。我会考虑使用 deleteLater(),或者,可能更好的是,一个 QueuedConnection 用于信号/插槽连接。
  • 通用汽车“但是请注意,在 QObject * 上调用 delete 通常是一个坏主意”。我不同意,删除通常很好,默认情况下使用 deleteLater() 而不考虑使代码变得更不可预测,并且可能隐藏问题并创建新问题(例如,如果对象拥有资源或继续做某事,则在 deleteLater 之间() 和实际删除)。
  • deleteLater 不能解决问题。我会看看我是否可以组合一个最小的可重现示例,但又是 C++ 和 Qt,所以“最小”并不意味着太多......

标签: c++ qt segmentation-fault valgrind


【解决方案1】:

尝试这样做:

  • 首先将成员 QMetaObject::Connection foo_barConnect; 添加到您的班级。

  • 在构造函数中,将您的connect 保存在foo_barConnect 中:

foo_barConnect = connect(foo, SIGNAL(currentIndexChanged(int)), SLOT(bar(int)));
  • update()函数中,在delete之前添加:
// Check if `foo_barConnect` is valid (connected)
if(foo_barConnect){
    // Disconnect foo_bar connection
    disconnect(foo_barConnect);
}
  • 如果您重新new 对象并且需要连接,请不要忘记重新connect 并将连接存储在foo_barConnect 中。

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多