在Qt中,从对象的析构函数发射信号是否可行?

14

当一个继承自QObject的对象被销毁时,从它的析构函数中发射信号是否可行?我尝试了一下,似乎可以工作,但我不确定是否应该这样做。

例如,以下代码:

class MyClass : public QObject {
signals:
    void mySignal(const QString &str);
public:
    QString myString;
    ~MyClass() { emit mySignal(myString); }
}

在连接的槽函数执行时,可能会超出范围的对象,可以传递一个const引用。

2个回答

14

通常情况下,发射信号的方式是没问题的(QObject也用“destroyed”信号来实现),包括您的情况。当连接是直接的时候,字符串仍然存在。而当它是QueuedConnection时,字符串会首先被复制到事件循环中。


但我认为sender()方法可能会给出一个无效的指针?不管怎样,我想我应该小心谨慎,在编写槽时牢记这一点。 - sashoalm
3
好的。如果您有一个排队的连接,在析构函数中发出信号时,您不能使用sender()。信号的接收者必须意识到这一点。 - Johannes Schaub - litb
@JohannesSchaub-litb 这个真的普遍适用吗?为什么?想象一种情况,一个对象必须在某处(反)注册自己并获得回调。数据尚未释放。我必须确保发出此信号是直接连接,在 dtor 开始时发出,并且派生类将不会中断,但回调将正常工作,对吗? - ManuelSchneid3r

6

如果你问能不能这样做:是的,在本身上不会造成任何问题。

如果你问在Qt中这是否是一件通常安全的事情?绝对不安全。如果从析构函数中发出信号,你必须非常注意你的操作,并且要对Qt事件系统有很好的理解。

请记住,当一个继承自 QObject 的对象被销毁时,它会断开所有连接的信号,因此已经被销毁的对象不会再接收到任何调用其槽函数的请求。但是有一个例外情况:销毁顺序。当销毁链中的对象接收到事件时,可能会导致访问无效,因为虚拟函数和已经被销毁的后代成员变量已经不存在了。如果使用事件系统,下列任何条件都满足时,可能存在此类情况:

  • 在多线程环境中,如果对象没有在其自己的线程上被销毁。
  • 在多线程环境中,如果对象的销毁链触发了任何运行路径上的 processEvents()
  • 在多线程环境中,如果任何另一个线程上的对象与此对象有直接连接,并且未能对其销毁信号进行反应。
  • 在单线程环境中,当析构函数发送信号时,在直接连接链中可能会返回到对象。

我称之为“死亡期间的生命”,在析构函数中发出信号或运行任何形式的 processEvents()(通常是意外操作)会增加出现此类错误的可能性。

当然,如果你能够确保不存在任何当前或未来的代码将在销毁过程中触发任何槽函数,那么在析构函数中发出信号是完全安全的。但是很难给出这样的保证,因此建议尽量避免在析构函数中发出信号。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接