Sketch
The function of the interface is to provide a way to interact with other systems. Other systems don't need (or can't) know the details inside, but can only communicate with each other through the interface provided outside.
Pure virtual functions (slots are no exception) are easy to understand, so what about signals?
Is it valid to define a pure virtual signal in Qt?
Indeed, this topic is very interesting... Usually, we will define some pure virtual slot functions, but the topic of pure virtual signal is seldom discussed. So, can the signal be pure and imaginary?
Copyright: Once you go to Meng San Li, please indicate the source of the reprint. http://blog.csdn.net/liang19890820
Some assumptions about pure emptiness
Some understandings about signal and pure virtual:
- Signals will never be implemented (that is, signals are defined in. h and not required in. cpp)
- The main purpose of declaring a pure virtual function is to force inherited classes to provide an implementation.
Signals are not implemented. If they are declared to be pure virtual, they need inherited classes to provide an implementation, which directly conflicts with the "signal is not implemented". It's like having one person in two places at the same time. It's impossible. Therefore, it seems to be a mistake to declare a pure false signal.
In such programming, what if the interaction between signal slots is to be done? To be able to interact, most people may write code similar to the following:
Regrettably, Qt issued a warning:
warning: Signals cannot be declared virtual
So, how to solve this problem?
Solution
Here are three solutions:
- Derived from QObject, signals do not use virtual
- Define "signal" as a pure virtual function
- Define the connection mode of the signal slot in the interface
The test results are as follows:
Derived from QObject, signals do not use virtual
Let's not use virtual, well, that's not necessary, this is the simplest solution!
#ifndef CLOCK_H
#define CLOCK_H
#include <QObject>
class IClock : public QObject
{
Q_OBJECT
public:
~IClock() {}
virtual void doSomething() = 0;
signals:
void alarm();
};
#endif // CLOCK_H
Specific implementation is also independent of the signal, only need to achieve other interfaces can:
#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H
#include "clock.h"
#include <QObject>
#include <QtDebug>
class DigitalClock : public IClock
{
Q_OBJECT
public:
DigitalClock() {}
void doSomething() Q_DECL_OVERRIDE {
qDebug() << "Do something...";
}
};
#endif // DIGITAL_CLOCK_H
But this approach is not ideal, because in general, the interface is a simple class and does not need to be derived from QObject.
Define "signal" as a pure virtual function
"Signal" is defined as a pure virtual function:
#ifndef CLOCK_H
#define CLOCK_H
class IClock
{
public:
~IClock() {}
virtual void doSomething() = 0;
// Not a signal (but can be used as a signal), because IClock is not QObject
virtual void alarm() = 0;
};
#endif // CLOCK_H
Note: For IClock, alarm() is a pure virtual function, not a "signal" (but can be used as a signal), because IClock is not a QObject.
Specific implementations need to be derived from QObject (because signals are needed), and alarm() needs to be defined as a signal:
#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H
#include "clock.h"
#include <QObject>
#include <QtDebug>
class DigitalClock : public QObject, public IClock
{
Q_OBJECT
public:
DigitalClock() {}
void doSomething() Q_DECL_OVERRIDE {
qDebug() << "Do something...";
}
signals:
// Implementation is done by moc
void alarm() Q_DECL_OVERRIDE;
};
#endif // DIGITAL_CLOCK_H
At this time, the specific implementation of alarm() is accomplished by moc internally.
To test the validity, define a class - monitor.h:
#ifndef MONITOR_H
#define MONITOR_H
#include <QObject>
#include <QtDebug>
#include "clock.h"
class Monitor : public QObject
{
Q_OBJECT
private slots:
void onAlarm() {
qDebug() << "Get up";
}
public:
void monitorAlarm(IClock *clock) {
QObject *clockObject = dynamic_cast<QObject *>(clock);
if (clockObject) {
connect(clockObject, SIGNAL(alarm()), this, SLOT(onAlarm()));
} else {
qWarning() << "cannot monitor Alarm";
}
}
};
#endif // MONITOR_H
Note: When connecting signals, you need to convert them to QObject.
Testing in main.cpp:
#include <QCoreApplication>
#include "digital_clock.h"
#include "monitor.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
DigitalClock clock;
Monitor monitor;
monitor.monitorAlarm(&clock);
emit clock.alarm();
return a.exec();
}
Define the connection mode of the signal slot in the interface
In addition to the above, the connection mode of the signal slot can also be defined in the interface.
First, define an interface to connect the signal slot - connectToAlarm():
#ifndef CLOCK_H
#define CLOCK_H
#include <QObject>
class IClock
{
public:
~IClock() {}
virtual void doSomething() = 0;
virtual bool connectToAlarm(QObject *receiver, const char *pszSlot, bool isConnect) const = 0;
};
#endif // CLOCK_H
Then, the specific connection of the signal slot is realized in the derived class:
#ifndef DIGITAL_CLOCK_H
#define DIGITAL_CLOCK_H
#include "clock.h"
#include <QObject>
#include <QtDebug>
class DigitalClock : public QObject, public IClock
{
Q_OBJECT
public:
DigitalClock() {}
void doSomething() Q_DECL_OVERRIDE {
qDebug() << "Do something...";
}
bool connectToAlarm(QObject *receiver, const char *pszSlot, bool isConnect) const Q_DECL_OVERRIDE{
if (isConnect)
return connect(this, SIGNAL(alarm()), receiver, pszSlot);
return disconnect(this, SIGNAL(alarm()), receiver, pszSlot);
}
signals:
void alarm();
};
#endif // DIGITAL_CLOCK_H
Since then, the connection has become much simpler.
#ifndef MONITOR_H
#define MONITOR_H
#include <QObject>
#include <QtDebug>
#include "clock.h"
class Monitor : public QObject
{
Q_OBJECT
public slots:
void onAlarm() {
qDebug() << "Get up";
}
public:
void monitorAlarm(IClock *clock) {
bool isConnect = clock->connectToAlarm(this, SLOT(onAlarm()), true);
if (isConnect) {
qWarning() << "connectToAlarm";
} else {
qWarning() << "cannot monitor Alarm";
}
}
};
#endif // MONITOR_H