一般来说,接口应该是抽象类,它们的构造函数根本不应该做任何事情。所以这是糟糕的设计。
如果你坚持这样做,C++ 的语义会阻止我们完全按照你的说法去做。多重继承时,dynamic_cast 到 C 将失败,直到输入 C 的构造函数:
struct Interface {
Interface() { assert(dynamic_cast<QObject*>(this) == 0); }
};
struct C : public QObject, public Interface {
C() { assert(dynamic_cast<QObject*>(this)); }
};
因此,我们需要一些方法来延迟连接,直到构建完整的对象,或者至少直到它的 QObject 部分可用。
一种简单的方法是让接口明确要求构造基础:
struct Interface {
Interface(QObject * base) {
connect(base, ...);
}
};
struct C : public QObject, public Interface {
C() : Interface(this) {}
};
构造函数签名很好地表达了意图:Interface 旨在用于派生自QObject 的类。它不适用于那些不这样做的人。
另一种方法是延迟连接,直到事件循环有机会运行。如果不再需要连接,这是可以接受的。这不需要向Interface 构造函数传递指向基类的显式指针。
计时器归当前线程的事件调度程序所有,这样即使事件循环从未启动,它也不会泄漏。
class Interface {
public:
virtual void request() = 0; // entirely optional
Interface() {
auto timer = new QTimer(QAbstractEventDispatcher::instance());
timer.start(0);
QObject::connect(timer, &QTimer::timeout, [this, timer]{
timer.deleteLater();
connect(dynamic_cast<QObject*>(this), SIGNAL(request()), ...);
});
}
};
请注意,在所有情况下,在接口中将信号声明为虚拟是完全多余的。 Interface 的构造函数可以检查信号是否存在,并断言它,甚至总是abort()。
仅仅通过声明一个虚拟信号并不能保证该信号实际上是一个信号。即使编译器没有提供相反的诊断,以下内容也不起作用:
struct Interface {
signals:
virtual void aSignal() = 0;
};
struct Implementation : public QObject, public Interface {
void aSignal() {} // not really a signal!
};
这将在运行时检测丢失的信号:
struct Interface {
// No need for virtual signal!
Interface(QObject * base) {
Q_ASSERT(base->metaObject()->indexOfSignal("request()") != -1);
}
};
struct Implementation : public QObject, public Interface {
Q_OBJECT
Q_SIGNAL void reuest(); // a typo
Implementation() : Interface(this) {} // will assert!
};
确保遵循虚拟信号的最佳方法可能是两者都做,并将信号实现声明为覆盖:
struct Interface {
virtual void aSignal() = 0;
Interface(QObject * base) {
Q_ASSERT(base->metaObject()->indexOfSignal("request()") != -1);
}
};
struct Implementation : public QObject, public Interface {
Q_OBJECT
Q_SIGNAL void request() Q_DECL_OVERRIDE;
Implementation() : Interface(this) {}
};
编译器会捕捉到拼写错误即使您从未实例化Implementation,并且接口将在运行时检查实现的信号实际上是一个信号,而不是其他方法。
还必须注意Interface 的signals: 部分是伪造的。
signals 是一个带有空扩展的预处理器宏:对编译器来说,它什么都不做。
Interface 不是 QObject,因此被 moc 忽略。
-
signals: 仅对 moc 有意义,前提是它位于 both 的类中:
- 派生自
QObject,并且
- 包含
Q_OBJECT 宏。
一般来说,虚拟信号意义不大。它们都是“虚拟的”,因为您可以在不给编译器提供虚拟方法的情况下连接事物。
最后,它们也不适用于 Qt 5 编译时验证语法,因为 Interface 不是具有适当信号的具体 QObject-派生类