首页 文章

在C / Qt中定义纯虚拟信号是否有效?

提问于
浏览
13

我正在制作一个抽象基类,并且认为我可能想要一个纯粹的虚拟信号 . 但是当我编译时,我得到了我已经定义的纯虚拟信号的警告:

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

Is it valid to define a pure virtual signal in C++/Qt? Is it valid to define a virtual signal?

Qt's signal and slot documentation page表示您可以定义虚拟插槽,但似乎无法找到有关纯虚拟信号的良好信息 .

5 回答

  • 5
    • 信号没有实现[1](即你在.h文件中定义信号,然后在.cpp中没有实现) .

    • 声明函数pure virtual的主要目的是强制继承类提供实现 .

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

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

    So in conclusion it seems like declaring a "pure virtual" "signal" should be an error and thus not valid.


    在抽象基类的情况下,我认为这是正确的:

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

    [1]当我说“信号没有实现”时,我的意思是实现该类的人不提供实现 . 据我所知,在场景背后,Qt的moc在moc_FILE1.cpp中提供了一个实现 .

  • 0

    警告由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"
    
  • 0

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

  • 9

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

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

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

    • 派生类可能希望通过跳过基类实现来有选择地阻止发送信号 .

    • 派生类可能希望将其用作事件机制,并在将信号发送给侦听器之前或之后对信号作出反应 .

    这两种方案也可以用其他少OOP方式处理 .

相关问题