C++ 在传递的对象即将被销毁之前,发出作为参数传递QObject指针的信号安全吗? 让我们考虑这个简单的例子: Class Emitter: public QObject { ... signal: surfaceDestroyed(QObject*); public: void emittingMethod(QObject* surface) { emit surfaceDestroyed(surface); delete surface; } }

C++ 在传递的对象即将被销毁之前,发出作为参数传递QObject指针的信号安全吗? 让我们考虑这个简单的例子: Class Emitter: public QObject { ... signal: surfaceDestroyed(QObject*); public: void emittingMethod(QObject* surface) { emit surfaceDestroyed(surface); delete surface; } },c++,qt,signals-slots,C++,Qt,Signals Slots,我有一个排队连接用于此案例 connect(emitterObject, SIGNAL(surfaceDestroyed(QObject*), receiverObject, SLOT(onSurfaceDestroyed(QObject*)), Qt::QueuedConnection); 在onSurfaceDestroyed方法中,取消引用并使用接收到的QObject 所以问题是这个代码有多安全?我已经在QT网站和这里读了很多关于这个问题的信息,但我仍然没有对这个问题有一个清晰的

我有一个排队连接用于此案例

connect(emitterObject, SIGNAL(surfaceDestroyed(QObject*), receiverObject,
    SLOT(onSurfaceDestroyed(QObject*)), Qt::QueuedConnection);
在onSurfaceDestroyed方法中,取消引用并使用接收到的QObject

所以问题是这个代码有多安全?我已经在QT网站和这里读了很多关于这个问题的信息,但我仍然没有对这个问题有一个清晰的理解

在我看来,这段代码是不安全的,因为一旦事件循环被处理,并且接收方对象访问释放的内存,那么接收方对象的事件发生时,信号可以被发送,然后表面被破坏,因此SIGSEGV


这是实际代码的简化示例,因此很难跟踪是否会因此发生崩溃。

这取决于信号插槽连接的类型。如果发送方和接收方属于同一个线程,则默认情况下连接是直接的。在这种情况下,当您发出信号时,将立即调用插槽。当信号功能完成时,您可以确保插槽也已完成

当发送方和接收方属于不同的线程时,默认情况下连接将排队。在这种情况下,您的代码是不安全的。插槽的调用时间比信号的调用时间要晚得多。即使是
deleteLater
也不会保存这种情况,因为它是由发送方线程的事件循环处理的,不依赖于另一个线程的事件循环

因此,如果要编写这样的代码,请确保对象位于同一线程中。您可以将
Qt::DirectConnection
选项传递到
connect
函数,以使连接类型更加清晰

如果要使用排队连接,可以在发送方中发出例如
aboutToBeDeleted
信号。接收器将接收到该信号,以某种方式对其进行处理,并用触发实际对象删除的
cleanupdompled
信号进行响应


也考虑使用标准<代码> QObj::销毁< /代码>信号。它是在对象被销毁之前立即调用的,但在许多情况下可能很有用。

我认为调用
surface->deleteLater()
会更安全。好吧,根据经验,如果感觉不好,不要这样做让被毁对象的析构函数做任何你想做的事情不是更好吗?@vahancho是的,我想在后面的问题中提到delete,但最后我忘记了。它通常应该更安全,但我想100%确定这段代码是否会崩溃。为什么不能重写信号/插槽以不传递即将删除的对象?为什么不发射代码>销毁(qObj*)<代码>告诉另一个对象认为它被删除了?@韦斯米勒是的,你是对的,但是正如我提到的,真正的代码要复杂得多,所以我不能做所有的事情。easy@vahancho我不认为我仍然可以使用QObject,即发出destromed()信号来访问该对象的成员等,在我收到它的时候,来自QObject的派生类的所有析构函数都已经完成了它们的工作,对吗?destroyed()信号直接从QObject的dtor发出。@rightaway717如果使用直接连接,则可以使用
destroyed()
信号访问它作为参数发送的对象。如果您使用的是排队连接,那么在调用插槽之前,对象将被销毁。@thuga surface在我的例子中是从QObject派生的类的对象,让我们调用这个类surface。QObject dtor不是平凡的,所以~ Surface也被认为是不平凡的。按C++标准取消对象的指针,调用它的非平凡析构函数是UB。因此,我认为即使我有一个直接的联系,这仍然是UB。如果我不高兴,请纠正我wrong@rightaway717是的,将
QObject
指针转换到您自己的对象类型将失败。您只能使用
QObject
成员。