Подтвердить что ты не робот

Действительно ли для определения чистого виртуального сигнала в С++/Qt?

Я делаю абстрактно-базовый класс и думал, что мне нужен чистый виртуальный сигнал. Но когда я скомпилировал, я получил предупреждение для чистых виртуальных сигналов, которые я определил:

../FILE1.h:27: Warning: Signals cannot be declared virtual
../FILE1.h:28: Warning: Signals cannot be declared virtual

Можно ли определить чистый виртуальный сигнал в С++/Qt? Действительно ли для определения виртуального сигнала?

Страница документации по каналу Qt и слотам говорит, что вы можете определить виртуальные слоты, но не говорить о сигналах. Я не могу найти хорошую информацию о чистых виртуальных сигналах.

4b9b3361

Ответ 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

Два сценария, где имеет смысл виртуальный сигнал:

  • Производный класс может захотеть выборочно заблокировать отправку сигнала, пропустив реализацию базового класса.
  • Производный класс может использовать его как механизм событий и реагировать на сигнал до или после отправки его слушателям.

Оба сценария также могут обрабатываться другими способами с меньшим ООП.