c++qtqt5qt-slot

Is it safe to connect a signal to a pure virtual slot in a constructor of a base class?


I ask myself if the following code is safe :

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QTimer>

class Base : public QObject
{
    Q_OBJECT

public:
    Base()
    {
        // is it safe to do that ?
        connect(this, SIGNAL(signal1()), this, SLOT(slot1()));
    }
    virtual ~Base() {}

signals:
    void signal1();

public slots:
    virtual void slot1() = 0; // could be only virtual
};

class Derived : public Base
{
    Q_OBJECT

public slots:
    virtual void slot1()
    {
        qDebug() << "derived slot";
    }

    void emitSignal1()
    {
        emit signal1();
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Derived d;

    QTimer::singleShot(0, &d, SLOT(emitSignal1()));

    return a.exec();
}

#include "main.moc"

The output is as expected :

derived slot

I haven't look at what the connect method does behind the scene, but i guess it's something like setting a callback function.

If there is no call to a virtual method in the Base class constructor, then i don't see any side effects so far but is it true ?

Thank you.


Solution

  • There is no problem, because the call to the slot is not given in the constructor or destructor of the class but it is called by the event loop, and that can be observed in the .moc:

    void Base::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
    {
        if (_c == QMetaObject::InvokeMetaMethod) {
            Base *_t = static_cast<Base *>(_o);
            Q_UNUSED(_t)
            switch (_id) {
            case 0: _t->signal1(); break;
            case 1: _t->slot1(); break;
            default: ;
            }
        } else if (_c == QMetaObject::IndexOfMethod) {
            int *result = reinterpret_cast<int *>(_a[0]);
            {
                using _t = void (Base::*)();
                if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Base::signal1)) {
                    *result = 0;
                    return;
                }
            }
        }
        Q_UNUSED(_a);
    }
    

    On the other hand it is advisable to use the new connection syntax:

    # ...
    connect(this, &Base::signal1, this, &Base::slot1);
    # ...
    QTimer::singleShot(0, &d, &Derived::emitSignal1);
    # ...