4

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Fryz
  • 2,119
  • 2
  • 25
  • 45
  • 1
    This should be fine, as long as you stick with the "old-style" connect syntax (using `SIGNAL` and `SLOT`). The Qt5 version using function pointers will not work. – Felix Nov 28 '18 at 21:23
  • Could you please explain why it "won't work" with the new connection syntax ? Because the answer of @eyllanesc suggests to use it. – Fryz Nov 29 '18 at 08:16
  • 1
    I remember getting a compiler error trying to do so a while ago. But with my current compiler it does indeed work. So yes, @eyllanesc's answer is correct. It does work, as long as your compiler accepts the use of pure virtual members in the constructor - and thus you *should* indeed use the new syntax. – Felix Nov 29 '18 at 09:40

1 Answers1

3

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);
# ...
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • 2
    Your answer is correct, but you should hint that this only works, as long as the signal itself is not emitted from the constructor. Calling `connect(...); emit signal1();` Will terminate the application due to a "pure virtual method called" exception – Felix Nov 29 '18 at 09:43