我想连接一些从接口类派生的对象信号。
连接在QWidget::listenToAnimal(AnimalInterface*)
中完成。
这不起作用,因为qt_metacall is not a member of 'AnimalInterface'
和static assertion failed: No Q_OBJECT in the class with the signal
。
当然AnimalInterface
没有Q_OBJECT宏,也没有继承QObject,因为它是一个接口......
我想通过接口类进行连接,因为我不想手动为Cat
和Dog
重新键入相同的代码。
是否可以按照我想要的方式连接信号?也许有模板?这可能是lambda
特定的问题吗?
头:
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class AnimalInterface{
public:
virtual ~AnimalInterface();
virtual void makeSound() = 0;
/*signals*/
virtual void madeSound() = 0;
};
Q_DECLARE_INTERFACE(AnimalInterface,"interface")
class Dog : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Cat : public QObject, public AnimalInterface
{
Q_OBJECT
Q_INTERFACES(AnimalInterface)
public:
void makeSound();
signals:
void madeSound();
};
class Widget : public QWidget
{
Q_OBJECT
Cat *cat_;
Dog *dog_;
public:
Widget(QWidget *parent = 0);
~Widget();
void listenToAnimal(AnimalInterface *animal);
};
#endif // WIDGET_H
CPP:
#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
dog_ = new Dog;
cat_ = new Cat;
listenToAnimal(dog_);
listenToAnimal(cat_);
dog_->makeSound();
cat_->makeSound();
}
void Widget::listenToAnimal(AnimalInterface *animal)
{
connect(animal, &AnimalInterface::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
Widget::~Widget()
{
}
void Cat::makeSound()
{
qDebug()<<"Cat says miaow";
emit madeSound();
}
void Dog::makeSound()
{
qDebug()<<"Dog says wuff";
emit madeSound();
}
的main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
答案 0 :(得分:2)
由于您在编译类型中知道派生类型,因此可以连接到正确的,静态已知的QObject
派生类型。不需要动态铸造或任何类型的东西。您只是不希望listenToAnimal
方法可用于非AnimalInterface
- 继承类型,即使它们具有兼容的madeSound
方法:
#include <type_traits>
template< class T,
typename =
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value>::type >
void listenToAnimal(T * animal) {
connect(animal, &T::madeSound, this, []{ qDebug() << "animal made sound"; });
}
template <class T>
void listenToAnimal(T * animal) {
Q_UNUSED(static_cast<AnimalInterface*>(animal));
connect(animal, &T::madeSound, this, &Widget::onAnimalMadeSound);
}
然后您可以使用它而无需拼出类型 - 它已经为编译器所知:
listenToAnimal(dog_);
listenToAnimal(cat_);
如果在编译时未知派生类型,则必须动态转换为QObject
并按名称连接,而不是通过方法指针连接。如果你传入错误的类型,它会在运行时断言 - 毕竟,它不足以成为AnimalInterface
的实例,它也需要是QObject
实例。
void listenToAnimal(AnimalInterface * animal) {
auto object = dynamic_cast<QObject*>(animal);
Q_ASSERT(object);
connect(object, SIGNAL(madeSound()), this, SLOT(onAnimalMadeSound()));
}
类型AnimalInterface
具有虚拟madeSound
方法的事实在某种程度上是相关的 - 它保证派生类使用这样的签名实现方法。但是,它并不能保证该方法是一种信号。所以你应该重新考虑你的设计并问问自己:&#34;当我无法真正用它进行静态类型检查时,我可以通过使用静态类型系统获得什么?&#34;
最有可能你应该制作任何名义上接受AnimalInterface*
的方法,进行参数化并获取指向具体类的指针。如果类型擦除导致相同的机器代码,现代代码生成器和链接器将对这些代码进行重复数据删除。
答案 1 :(得分:0)
找到一个包含模板的解决方案。我第一次尝试没有工作,显然先做错了。这就是......
只需替换问题中的示例中的相应部分(并从源文件中删除listenToAnimal
的定义):
头:
template<class T>
void listenToAnimal(AnimalInterface *animal)
{
T *animal_derivate = dynamic_cast<T*>(animal);
if (animal_derivate){
connect(animal_derivate, &T::madeSound,
this,
[](){
qDebug()<<"animal made sound";
});
}
}
CPP:
listenToAnimal<Dog>(dog_);
listenToAnimal<Cat>(cat_);
<强>更新强>
在尝试了Kuba Ober的回答后,现在看起来效果最好:
template<typename T>
typename std::enable_if<std::is_base_of<AnimalInterface, T>::value,void>::type
listenToAnimal(T *animal)
{
connect(animal, &T::madeSound, this, [](){ qDebug()<<"animal made sound"; });
}
然而,如果我创建像AnimalInterface *bird = new Bird
这样的动物,那么仍然无法正常工作的一点是因为它会抛出基类没有信号的相同错误。