15

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

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

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

Qt 的信号和槽文档页面说您可以定义虚拟槽,但不谈论信号。我似乎找不到关于纯虚拟信号的好信息。

4

5 回答 5

9
  • 信号从来没有实现[1](即,您在 .h 文件中定义信号,然后在 .cpp 中没有实现)。
  • 声明一个纯虚函数的主要目的是强制继承类提供一个实现。

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

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

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


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

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

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

于 2012-04-05T13:35:20.933 回答
7

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

虚拟信号的唯一有效用途是在声明不派生自的抽象接口时,QObject如此出色答案中所述。这种方法没有错。Moc 试图提供帮助,因为在大多数情况下,虚拟信号是错误的。

即使那样,没有收到警告的简单解决方法是跳过signals:界面中的关键字。这是完全没有必要的,因为接口不是从 moc 派生的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"
于 2015-06-26T17:13:38.847 回答
2

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

于 2012-04-05T13:05:38.327 回答
0

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

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);
}
于 2014-04-22T09:26:30.300 回答
0

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

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

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

于 2015-11-30T22:26:16.697 回答