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 には実装がありません)。
  • 関数を純粋仮想として宣言する主な目的は、継承クラスに強制的に実装を提供させることです。

上記の2つのステートメントを考えると、私の考えは次のとおりです。

シグナルには実装がありませんが、それを純粋仮想として宣言するには、継承クラスが実装を提供する必要があります...これは、「シグナルに実装がありません」と直接競合します。それは、誰かに同時に 2 つの場所にいるように頼むようなものです。

したがって、結論として、「純粋な仮想」「信号」を宣言することはエラーであり、したがって無効であるように思われます。


抽象基本クラスの場合、私が正しいと思うのは次のとおりです。

関数を「仮想」のみとして宣言すると、それでも警告が表示されます。警告を回避するための解決策は、信号を「仮想」または「純粋仮想」で修飾しないことだと思います。継承クラスは信号を宣言しませんが、基本クラスで定義された信号を発信できます。

[1] 「シグナルには実装がない」と言うとき、クラスを実装する人が実装を提供しないことを意味します。舞台裏で Qt の moc が moc_FILE1.cpp に実装を提供していることを理解しています。

于 2012-04-05T13:35:20.933 に答える
7

この警告は、C++ コンパイラではなく moc によって報告され、抽象インターフェイスの特定のケースを除いて有効です。

この優れた回答で詳しく説明されてQObjectいるように、仮想シグナルの唯一の有効な使用法は、から派生していない抽象インターフェイスを宣言する場合です。そのアプローチには何の問題もありません。ほとんどの場合、仮想信号は間違いであるため、Moc は役に立ちます。

それでも、警告を受け取らないsignals:ようにするための簡単な回避策は、インターフェイスでキーワードをスキップすることです。インターフェイスは moc から派生していないため、QObjectmoc によってまったく処理されるべきではないため、完全に不要です。

// 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

仮想信号が意味を持つ 2 つのシナリオ:

  1. 派生クラスは、基本クラスの実装をスキップすることによって、シグナルの送信を選択的にブロックしたい場合があります。
  2. 派生クラスは、それをイベント メカニズムとして使用し、リスナーへの送信前または送信後にシグナルに反応する場合があります。

どちらのシナリオも、OOP の少ない他の方法で処理できます。

于 2015-11-30T22:26:16.697 に答える
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 に答える