【问题标题】:Is it valid to define a pure virtual signal in C++/Qt?在 C++/Qt 中定义纯虚拟信号是否有效?
【发布时间】:2012-04-19 05:22:51
【问题描述】:

我正在创建一个抽象基类,并在想我可能想要一个纯虚拟信号。但是当我编译时,我收到了我定义的纯虚拟信号的警告:

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

在 C++/Qt 中定义纯虚信号有效吗?定义虚拟信号有效吗?

Qt's signal and slot documentation page 说您可以定义虚拟插槽,但不谈论信号。我似乎找不到关于纯虚拟信号的好信息。

【问题讨论】:

标签: c++ qt abstract-class signals-slots


【解决方案1】:

我认为拥有(纯)虚拟信号毫无意义。提供给 Qt 的 signals 宏简单地扩展为 protected,因此您声明的所有信号实际上都是受保护方法的声明。 moc 生成的代码将提供这些函数的实现。

【讨论】:

    【解决方案2】:
    • 信号没有实现[1](即,您在 .h 文件中定义信号,然后在 .cpp 中没有实现)。
    • 声明纯虚函数的主要目的是强制继承类提供实现。

    鉴于以上两个陈述,这是我的想法:

    信号没有实现,但将其声明为纯虚拟将需要继承类提供实现......这与“信号没有实现”直接冲突。这就像要求某人同时在两个地方是不可能的。

    因此,总而言之,似乎声明“纯虚拟”“信号”应该是错误的,因此无效。


    对于抽象基类,我认为是正确的:

    当一个人声明这个函数只是“虚拟的”时,它仍然会给出警告。为了避免任何警告,我认为解决方案是不使用任何“虚拟”或“纯虚拟”来限定信号,然后继承类不会声明任何信号,但仍然可以发出基类中定义的信号。

    [1] 当我说“信号永远没有实现”时,我的意思是实现类的人没有提供实现。我知道在幕后 Qt 的 moc 在 moc_FILE1.cpp 中提供了一个实现。

    【讨论】:

      【解决方案3】:

      有一种解决方案可以创建纯虚函数,它将给定的插槽连接到信号,反之亦然。例如:

      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);
      }
      

      【讨论】:

      • 不过,This 是一个更好的解决方案。
      【解决方案4】:

      警告是由 moc 报告的,而不是由 C++ 编译器报告的,它是有效的,除了抽象接口的特定情况。

      虚拟信号的唯一有效用途是在声明不是从QObject 派生的抽象接口时使用detailed in this excellent answer。这种方法没有错。 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's 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"
      

      【讨论】:

        【解决方案5】:

        虚拟信号有意义的两种情况:

        1. 派生类可能希望通过跳过基类实现来选择性地阻止信号的发送。
        2. 派生类可能希望将其用作事件机制,并在将信号发送给侦听器之前或之后对信号做出反应。

        这两种情况也可以用其他较少 OOP 的方式来处理。

        【讨论】:

          猜你喜欢
          • 2011-03-01
          • 2010-09-25
          • 2017-05-24
          • 2011-12-25
          • 2011-04-19
          • 2015-10-13
          • 2015-02-22
          • 1970-01-01
          相关资源
          最近更新 更多