diff --git a/src/corelib/doc/src/includes/qobject.qdocinc b/src/corelib/doc/src/includes/qobject.qdocinc index 65630c82ee4..d3607582e92 100644 --- a/src/corelib/doc/src/includes/qobject.qdocinc +++ b/src/corelib/doc/src/includes/qobject.qdocinc @@ -8,3 +8,12 @@ affect classes that rely on this signal for cleaning up resources. It is recommended to disconnect only the specific signals that were connected by application code. //! [disconnect-all] + +//! [disconnect-mismatch] +\note Use the the same syntax, pointer-to-member-function or string-based using +the \c SIGNAL and \c SLOT macros, in connect() and corresponding disconnect() +calls. + +To avoid mismatches, store the connection handle returned by connect(), and use +it in the call to \l{disconnect(const QMetaObject::Connection &connection)}{disconnect()}. +//! [disconnect-mismatch] diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 31b779dc9b3..7a0b2ce82b5 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -3210,6 +3210,8 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho \endlist + \include includes/qobject.qdocinc disconnect-mismatch + \nullptr may be used as a wildcard, meaning "any signal", "any receiving object", or "any slot in the receiving object", respectively. @@ -3362,6 +3364,8 @@ bool QObject::disconnect(const QObject *sender, const char *signal, \endlist + \include includes/qobject.qdocinc disconnect-mismatch + QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object". In the same way \nullptr can be used for \a receiver in the meaning "any receiving object". In this case method should also be QMetaMethod(). \a sender parameter should be never \nullptr. @@ -3435,6 +3439,8 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, Disconnects \a signal from \a method of \a receiver. + \include includes/qobject.qdocinc disconnect-mismatch + A signal-slot connection is removed when either of the objects involved are destroyed. @@ -3448,6 +3454,8 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, Disconnects all signals in this object from \a receiver's \a method. + \include includes/qobject.qdocinc disconnect-mismatch + A signal-slot connection is removed when either of the objects involved are destroyed. */ @@ -5384,7 +5392,12 @@ bool QObject::disconnect(const QMetaObject::Connection &connection) \note It is not possible to use this overload to disconnect signals connected to functors or lambda expressions. That is because it is not possible to compare them. Instead, use the overload that takes a - QMetaObject::Connection + QMetaObject::Connection. + + \note Unless \a method is \nullptr, this function will also not break + connections that were made using the string-based version of connect(). To + break such connections, use the corresponding string-based overload of + disconnect(). \sa connect() */ diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 6c387fde963..4f36e142794 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -110,6 +110,7 @@ private slots: void baseDestroyed(); void pointerConnect(); void pointerDisconnect(); + void mixedConnectDisconnect(); void emitInDefinedOrderPointer(); void customTypesPointer(); void connectCxx0x(); @@ -4852,6 +4853,125 @@ void tst_QObject::pointerDisconnect() QVERIFY(!r2.called(2)); } +struct MixingFunctor +{ + int index; + void operator()() { s_called[index] = true; } + bool called() const { return s_called[index]; } + void reset() { s_called[index] = false; } + +private: + static inline bool s_called[5] = {}; +}; + +void tst_QObject::mixedConnectDisconnect() +{ + SenderObject s; + ReceiverObject pmf; + ReceiverObject named; + + MixingFunctor functor1{1}; + MixingFunctor functor2{2}; + MixingFunctor functor3{3}; + MixingFunctor functor4{4}; + + auto makePMFConnections = [sender = &s](const ReceiverObject *receiver){ + QObject::connect(sender, &SenderObject::signal1, receiver, &ReceiverObject::slot1); + QObject::connect(sender, &SenderObject::signal2, receiver, &ReceiverObject::slot2); + QObject::connect(sender, &SenderObject::signal3, receiver, &ReceiverObject::slot3); + QObject::connect(sender, &SenderObject::signal4, receiver, &ReceiverObject::slot4); + }; + auto makeNamedConnections = [sender = &s](const ReceiverObject *receiver){ + QObject::connect(sender, SIGNAL(signal1()), receiver, SLOT(slot1())); + QObject::connect(sender, SIGNAL(signal2()), receiver, SLOT(slot2())); + QObject::connect(sender, SIGNAL(signal3()), receiver, SLOT(slot3())); + QObject::connect(sender, SIGNAL(signal4()), receiver, SLOT(slot4())); + }; + + makePMFConnections(&pmf); + makeNamedConnections(&named); + QObject::connect(&s, &SenderObject::signal1, &named, functor1); + QObject::connect(&s, &SenderObject::signal2, &named, functor2); + QObject::connect(&s, &SenderObject::signal3, &named, functor3); + QObject::connect(&s, &SenderObject::signal4, &named, functor4); + + // sanity check + s.emitSignal1(); + QVERIFY(pmf.called(1)); + QVERIFY(named.called(1)); + QVERIFY(functor1.called()); + pmf.reset(); + named.reset(); + functor1.reset(); + + // disconnecting a string-based connection with PMF-based disconnect doesn't work + bool ret = false; + ret = QObject::disconnect(&s, &SenderObject::signal1, &pmf, &ReceiverObject::slot1); + QVERIFY(ret); + ret = QObject::disconnect(&s, &SenderObject::signal1, &named, &ReceiverObject::slot1); + QEXPECT_FAIL("", "Mixing PMF connect with string-based disconnect doesn't work", Continue); + QVERIFY(ret); + + s.emitSignal1(); + QVERIFY(!pmf.called(1)); + QEXPECT_FAIL("", "Mixing PMF connect with string-based disconnect doesn't work", Continue); + QVERIFY(!named.called(1)); + // cannot disconnect a specific functor (or lambda) + QVERIFY(functor1.called()); + pmf.reset(); + named.reset(); + functor1.reset(); + + // wildcard disconnect works even in mixed cases and for functor objects + ret = QObject::disconnect(&s, SIGNAL(signal2()), &pmf, nullptr); + QVERIFY(ret); + ret = QObject::disconnect(&s, &SenderObject::signal2, &named, nullptr); + QVERIFY(ret); + + s.emitSignal2(); + QVERIFY(!pmf.called(2)); + QVERIFY(!named.called(2)); + QVERIFY(!functor2.called()); + pmf.reset(); + named.reset(); + functor2.reset(); + + ret = QObject::disconnect(&s, &SenderObject::signal3, nullptr, nullptr); + QVERIFY(ret); + + s.emitSignal3(); + QVERIFY(!pmf.called(3)); + QVERIFY(!named.called(3)); + QVERIFY(!functor3.called()); + pmf.reset(); + named.reset(); + functor3.reset(); + + // disconnect() member function is only available for string-based syntax + // and doesn't disconnect PMF-based connections + ret = s.disconnect(&pmf, SLOT(slot4())); + QEXPECT_FAIL("", "Mixing PMF connect with string-based disconnect doesn't work", Continue); + QVERIFY(ret); + s.emitSignal4(); + QEXPECT_FAIL("", "Mixing PMF connect with string-based disconnect doesn't work", Continue); + QVERIFY(!pmf.called(4)); + // the functor gets of course still called + QVERIFY(functor4.called()); + + pmf.reset(); + named.reset(); + functor4.reset(); + + ret = s.disconnect(&named, nullptr); + QVERIFY(ret); + + s.emitSignal4(); + QVERIFY(!named.called(4)); + QVERIFY(!functor4.called()); + named.reset(); + functor4.reset(); +} + void tst_QObject::emitInDefinedOrderPointer() {