Действительно ли для определения чистого виртуального сигнала в С++/Qt?
Я делаю абстрактно-базовый класс и думал, что мне нужен чистый виртуальный сигнал. Но когда я скомпилировал, я получил предупреждение для чистых виртуальных сигналов, которые я определил:
../FILE1.h:27: Warning: Signals cannot be declared virtual
../FILE1.h:28: Warning: Signals cannot be declared virtual
Можно ли определить чистый виртуальный сигнал в С++/Qt? Действительно ли для определения виртуального сигнала?
Страница документации по каналу Qt и слотам говорит, что вы можете определить виртуальные слоты, но не говорить о сигналах. Я не могу найти хорошую информацию о чистых виртуальных сигналах.
Ответы
Ответ 1
- Сигналы не имеют реализации [1] (т.е. вы определяете сигнал в вашем .h файле, а затем нет реализации в .cpp).
- Основная цель объявления функции pure virtual - заставить наследующий класс предоставить реализацию.
Учитывая приведенные выше два утверждения, я думаю:
Сигналы не имеют реализации, но при объявлении его чистым виртуальным потребует, чтобы класс наследования обеспечивал реализацию... которые непосредственно конфликтуют с "сигналами не имеют реализации". Это как просить кого-то быть в двух местах сразу, это просто невозможно.
Итак, в заключение кажется, что объявление "чистого виртуального" "сигнала" должно быть ошибкой и, следовательно, недействительным.
В случае абстрактного базового класса здесь я считаю правильным:
Когда объявляется функция только "виртуальная", она все же дает предупреждение. Чтобы избежать каких-либо предупреждений, я думаю, что решение состоит в том, чтобы не квалифицировать сигнал с помощью любого "виртуального" или "чистого виртуального", а затем наследующий класс не будет объявлять какие-либо сигналы, но все же может излучать сигналы, определенные в базовом классе.
[1], когда я говорю, что "сигналы никогда не имеют реализации", я имею в виду, что человек, реализующий класс, не обеспечивает реализацию. Я понимаю, что за сценой Qt moc обеспечивает реализацию в файле moc_FILE1.cpp.
Ответ 2
Предупреждение сообщается moc, а не компилятором С++, и оно допустимо, за исключением конкретного случая абстрактных интерфейсов.
Единственное допустимое использование для виртуальных сигналов - это объявление абстрактных интерфейсов, которые не выводятся из QObject
, как подробно в этом превосходном ответе. Там нет ничего плохого в этом подходе. Moc пытается быть полезным, поскольку в большинстве случаев виртуальный сигнал является ошибкой.
Даже тогда простым обходным путем для получения предупреждения является пропустить ключевое слово signals:
в интерфейсе. Это совершенно необязательно, так как интерфейс не получается из QObject
и поэтому не должен обрабатываться moc вообще:
// https://github.com/KubaO/stackoverflown/tree/master/questions/virtual-slot-10029130
#include <QtCore>
class IDogInterface {
public:
// no signals: section since it not a QObject!
virtual void barks() = 0; // a signal
};
class ADog : public QObject, public IDogInterface {
Q_OBJECT
public:
Q_SIGNAL void barks() override; // implementation is generated by moc
};
class Monitor : public QObject {
Q_OBJECT
int m_count{};
Q_SLOT void onBark() { m_count++; }
public:
int count() const { return m_count; }
void monitorBarks(IDogInterface * dog) {
QObject * dogObject = dynamic_cast<QObject*>(dog);
if (dogObject) {
connect(dogObject, SIGNAL(barks()), SLOT(onBark()));
} else {
qWarning() << "cannot monitor barking on dog instance" << (void*)dog;
}
}
};
int main() {
ADog dog;
Monitor monitor;
monitor.monitorBarks(&dog);
emit dog.barks();
Q_ASSERT(monitor.count() == 1);
}
#include "main.moc"
Ответ 3
Я думаю, что просто нет смысла иметь (чистые) виртуальные сигналы. макрос signals
, предоставляемый Qt, просто расширяется до protected
, поэтому все сигналы, которые вы объявляете, на самом деле являются декларациями защищенных методов. Код, созданный moc
, предоставит реализации этих функций.
Ответ 4
существует решение для создания чистой виртуальной функции, которая будет подключать данный слот к сигналу или наоборот. например:.
class IBaseInterface
{
public:
virtual bool connectToSignal1(QObject* pReceiver, const char* pszSlot, bool bConnect) const = 0;
};
class CDerived : public QObject, public IBaseInterface
{
Q_OBJECT
public:
virtual bool connectToSignal1(QObject* pReceiver, const char* pszSlot, bool bConnect) const;
signals:
void signal1(const QString& msg);
};
bool CDerived::connectToSignal1(QObject* pReceiver, const char* pszSlot, bool bConnect) const
{
if(bConnect)
return connect(this, SIGNAL(signal1(QString)), pReciever, pszSlot);
return disconnect(this, SIGNAL(signal1(QString)), pReciever, pszSlot);
}
далее в код клиента можно ввести:
class CSomeClass : public QObject
{
Q_OBJECT
protected /*or public, or private*/ slots:
void someSlot(const QString& msg);
};
void CSomeClass::somefunction()
{
IBaseInterface* p = new CDerived;
if (!p->connectToSignal1(this, SLOT(someSlot(QString)), true))
QMessageBox::warning(this, tr("Warning"), tr("Cannot connect ...."), QMessageBox::Ok);
}
Ответ 5
Два сценария, где имеет смысл виртуальный сигнал:
- Производный класс может захотеть выборочно заблокировать отправку сигнала, пропустив реализацию базового класса.
- Производный класс может использовать его как механизм событий и реагировать на сигнал до или после отправки его слушателям.
Оба сценария также могут обрабатываться другими способами с меньшим ООП.