C++ 在接口类中声明抽象信号
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17943496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Declare abstract signal in interface class
提问by Beachwalker
How to declare a Qt signal in an abstract class / interface when the implementing class is already derrived from QObject/QWidget?
当实现类已经从 QObject/QWidget 派生时,如何在抽象类/接口中声明 Qt 信号?
class IEmitSomething
{
public:
// this should be the signal known to others
virtual void someThingHappened() = 0;
}
class ImplementEmitterOfSomething : public QWidget, public IEmitSomething
{
// signal implementation should be generated here
signals: void someThingHappended();
}
回答by Beachwalker
As I found out in the last days... the Qt way of doing this is like this:
正如我在最后几天发现的那样......这样做的 Qt 方式是这样的:
class IEmitSomething
{
public:
virtual ~IEmitSomething(){} // do not forget this
signals: // <- ignored by moc and only serves as documentation aid
// The code will work exactly the same if signals: is absent.
virtual void someThingHappened() = 0;
}
Q_DECLARE_INTERFACE(IEmitSomething, "IEmitSomething") // define this out of namespace scope
class ImplementEmitterOfSomething : public QWidget, public IEmitSomething
{
Q_OBJECT
Q_INTERFACES(IEmitSomething)
signals:
void someThingHappended();
}
Now you can connect to those interface signals.
现在您可以连接到这些接口信号。
If you don't have access to the implementation when connecting to the signal your connect statement will require a dynamic cast to QObject
:
如果您在连接到信号时无权访问实现,您的 connect 语句将需要动态转换为QObject
:
IEmitSomething* es = ... // your implementation class
connect(dynamic_cast<QObject*>(es), SIGNAL(someThingHappended()), ...);
... and this way you're not forced to expose the implementation class to subscribers and clients. Yeah!!!
...这样您就不会被迫向订阅者和客户公开实现类。是的!!!
回答by Dmitry Sazonov
In Qt, "signals" is synonim for "protected". But it helps MOC to generate necessary code. So, if you require interface with some signals - you should declare them as virtual abstract protected methods. All neccessary code will be generated by MOC - you may see details, that "emit somesignal" will be replaced with virtual call of protected method with same name. Note, that the body of with method aslo generated by Qt.
在 Qt 中,“信号”是“受保护”的同义词。但它有助于 MOC 生成必要的代码。因此,如果您需要与某些信号接口 - 您应该将它们声明为虚拟抽象保护方法。所有必要的代码将由 MOC 生成 - 您可能会看到详细信息,“发出一些信号”将被替换为具有相同名称的受保护方法的虚拟调用。请注意,Qt 生成的 with 方法的主体也是如此。
UPDATE: Sample code:
更新:示例代码:
MyInterfaces.h
我的接口文件
#pragma once
struct MyInterface1
{
signals:
virtual void event1() = 0;
};
struct MyInterface2
{
signals:
virtual void event2() = 0;
};
MyImpl.h
MyImpl.h
#ifndef MYIMPL_H
#define MYIMPL_H
#include <QObject>
#include "MyInterfaces.h"
class MyImpl
: public QObject
, public MyInterface1
, public MyInterface2
{
Q_OBJECT
public:
MyImpl( QObject *parent );
~MyImpl();
void doWork();
signals:
void event1();
void event2();
};
class MyListner
: public QObject
{
Q_OBJECT
public:
MyListner( QObject *parent );
~MyListner();
public slots:
void on1();
void on2();
};
#endif // MYIMPL_H
MyImpl.cpp
MyImpl.cpp
#include "MyImpl.h"
#include <QDebug>
MyImpl::MyImpl(QObject *parent)
: QObject(parent)
{}
MyImpl::~MyImpl()
{}
void MyImpl::doWork()
{
emit event1();
emit event2();
}
MyListner::MyListner( QObject *parent )
{}
MyListner::~MyListner()
{}
void MyListner::on1()
{
qDebug() << "on1";
}
void MyListner::on2()
{
qDebug() << "on2";
}
main.cpp
主程序
#include <QCoreApplication>
#include "MyImpl.h"
int main( int argc, char *argv[] )
{
QCoreApplication a( argc, argv );
MyImpl *invoker = new MyImpl( NULL );
MyListner *listner = new MyListner( NULL );
MyInterface1 *i1 = invoker;
MyInterface2 *i2 = invoker;
// i1, i2 - not QObjects, but we are sure, that they will be.
QObject::connect( dynamic_cast< QObject * >( i1 ), SIGNAL( event1() ), listner, SLOT( on1() ) );
QObject::connect( dynamic_cast< QObject * >( i2 ), SIGNAL( event2() ), listner, SLOT( on2() ) );
invoker->doWork();
return a.exec();
}
回答by Reinstate Monica
There are two problems with declaring signals as abstract methods in interfaces:
在接口中将信号声明为抽象方法有两个问题:
A signal is a signal from Qt's viewpoint only when implemented in a particular way- namely, when the implementation is generated by moc, and is included in the metadata for the object.
It is usually bad design to emit signals directly from the outside of an object.
从 Qt 的角度来看,信号是仅在以特定方式实现时的信号- 即当实现由 moc 生成时,并包含在对象的元数据中。
直接从物体外部发射信号通常是糟糕的设计。
As a corollary, since the interface is abstract, you don't really need to declare its signals at all - it serves no purpose other than for documenting the intent, since:
作为推论,由于接口是抽象的,您根本不需要声明它的信号 - 除了记录意图外,它没有任何作用,因为:
If a signal is implemented in a class that derives from the interface, you can use the metaobject system to verify its presence.
You're not supposed to directly call these signal methods anyway.
Once you dynamically cast the non-object interface to
QObject
, it doesn't matter anymore that the implementation was deriving from the interface.
如果信号在派生自接口的类中实现,您可以使用元对象系统来验证它的存在。
无论如何,您不应该直接调用这些信号方法。
一旦您将非对象接口动态转换为
QObject
,实现是否从接口派生就不再重要了。
The only valid reasons left for doing such gymnastics would be to:
进行这种体操的唯一有效理由是:
Coax doxygen or another documentation generator into providing documentation for your code.
Force the concrete class to have an implementation of a method with the same name. This doesn't of course guarantee that it is in fact a signal.
哄骗 doxygen 或其他文档生成器为您的代码提供文档。
强制具体类具有同名方法的实现。这当然不能保证它实际上是一个信号。
回答by Bim
We all want to get rid of MOC for good, but until that happens I want to add an alternative that works without including QObject.h and without using Q_OBJECT and Q_INTERFACE in the interface class.
我们都想彻底摆脱 MOC,但在此之前,我想添加一个替代方案,该替代方案无需包含 QObject.h 且无需在接口类中使用 Q_OBJECT 和 Q_INTERFACE。
First define an abstract connect function in the interface:
首先在接口中定义一个抽象的connect函数:
class I_Foo
{
public:
virtual void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0;
};
Now in the derived class, override the function. Also declare the signal, add Q_OBJECT etc.
现在在派生类中,覆盖函数。还要声明信号,添加 Q_OBJECT 等。
class Bar : public QObject, public I_Foo
{
Q_OBJECT
public:
void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);
signals:
void A();
};
Then inside that classes .cpp do the connect:
然后在该类 .cpp 中进行连接:
Bar::connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType void type)
{
connect(this, SIGNAL(A()), receiver, method, type);
}
The caveat is, that you have to write the connect function in every derived class and you have to use the old-style-connect (or maybe use a template function), but that's about it.
需要注意的是,您必须在每个派生类中编写连接函数,并且必须使用旧式连接(或者可能使用模板函数),但仅此而已。