Details
-
Bug
-
Resolution: Invalid
-
P3: Somewhat important
-
None
-
5.4.1
-
None
Description
We've had a crash bug that has highlighted an inconsistency after a changing a connection from the syntax:
connect(foo, SIGNAL(mySignal()), bar, SLOT(mySlot()));
to:
connect(foo, &Foo::mySignal, bar, &Foo::mySlot);
Consider the following situation:
class Foo : public QWidget { Q_OBJECT public: Foo( QWidget* parent = 0 ) : QWidget(parent) { } ~Foo() { emit mySignal(); } public slots: void mySlot() { qDebug() << "mySlot is called"; }; signals: void mySignal(); }; Foo* bar = new Foo; Foo* foo = new Foo(bar); connect(foo, &Foo::mySignal, bar, &Foo::mySlot); // A. mySlot is called //connect(foo, SIGNAL(mySignal()), bar, SLOT(mySlot())); // B. replace connection with this and observe that mySlot isn't called delete bar;
To explain what's happening:
1. When you delete object bar, foo will get deleted in bar::~QWidget. This happens after bar::~Foo.
2. When we switched to the new connection syntax (A), Qt directly calls bar::myslot, even though the ~Foo destructor has been run. In our setup this resulted in a crash as the slot was using some deleted members.
3. However if you switch this to use the old connection syntax (B), when foo::mySignal fires, it will try and call bar::mySlot. However, this internally ends up invoking the virtual function Foo::qt_metacall on bar, and because bar::~Foo has already been executed, the vtable directs it to QWidget::qt_metacall instead. This results in no call to mySlot() as it doesn't find a slot with a matching ID. This behavior is good in that it doesn't crash, but I do think that there should be a warning here because the function wasn't actually invoked:
qobject.cpp, line 3731. metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); // should warn if return value >= 0.
Overall, this is a fairly subtle difference in behavior when using the new connection syntax, but an important one. There may be some mechanism to get consistent behavior (e.g. checking what the metaobject is at time of invocation), but at the least, I'd advise that this difference is documented.