2012-04-05 194 views
12

我正在制作一个抽象基类,并在想我可能想要一个纯虚拟信号。但是,当我整理我得到我所定义的纯虚信号警告:在C++/Qt中定义纯虚拟信号有效吗?

../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说你可以定义虚拟插槽,但不会谈论信号。我似乎无法找到纯虚拟信号的良好信息。

+0

[在不相关的说明Qt确实允许纯虚拟插槽......但没有提到纯虚拟信号。](http://stackoverflow.com/questions/2998216/does-qt-support-virtual-pure-slots ) – 2012-04-05 13:08:24

+1

为什么你认为你想让信号变为虚拟? – Chris 2012-04-05 14:05:54

+0

在此答案中给出了一种使用CRTP和泛型基类的不同方法(http://stackoverflow.com/a/32124726/1329652)。它允许容易地分解多个'QObject'派生类的通用代码,即使它们来自不同的基类(当然,它们不需要直接从'QObject'派生)。尽管如此, – 2015-08-20 17:51:22

回答

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

鉴于以上两个语句这里是我的想法:

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

因此,总之,似乎宣布“纯虚拟”“信号”应该是一个错误,因此无效。


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

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

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

2

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

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

[This](http://stackoverflow.com/a/18113601/1329652)是一个更好的解决方案。 – 2015-06-26 17:04:42

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" 
0

两种方案:

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

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