如何将抽象信号连接到接口构造函数中的插槽?
How to connect an abstract signal to a slot within the interface's constructor?
我有一个抽象 class,其中包含纯虚拟信号和从 QObject
派生的 class。我想将该信号连接到派生 class 的插槽。
class MSys : public QObject
{
Q_OBJECT
public:
explicit MSys(QObject *parent = 0) : QObject(parent) {}
virtual ~MSys() {}
public slots:
void onRequset();
};
class AbsView
{
protected:
AbsView() : m_sys(new MSys)
{
// QObject::connect(this, SIGNAL(request()), m_sys, SLOT(onRequset()));
/* What can I do here !? */
}
public:
virtual ~AbsView() {}
signals:
virtual void request() = 0;
private:
MSys *m_sys;
};
Q_DECLARE_INTERFACE(AbsView, "AbsView")
一旦构造函数完成,这就不是问题:然后 dynamic_cast<QObject*>(this)
将在任何接口的方法中工作。但是在构造函数中,这似乎是不可能的。
有什么办法吗?
一般来说,接口应该是抽象的 classes,它们的构造函数根本不应该做任何事情。所以这是糟糕的设计。
如果您坚持这样做,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
派生的 classes。它不适用于那些不适用的人。
另一种方法是延迟连接,直到事件循环有机会 运行。如果在此之前不需要连接,这是可以接受的。这不需要向 Interface
构造函数传递一个指向基 class.
的显式指针
计时器由当前线程的事件调度程序拥有,这样即使事件循环从未启动它也不会泄漏。
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 有意义,前提是它在 class 中 both:
- 源自
QObject
,并且
- 包含
Q_OBJECT
宏。
一般来说,虚拟信号没有什么意义。它们都是 "virtual",因为你可以在不给编译器一个虚拟方法的情况下连接事物。
最后,它们也不适用 Qt 5 编译时验证的语法,因为 Interface
不是具体的 QObject
-派生 class 并带有适当的信号
我有一个抽象 class,其中包含纯虚拟信号和从 QObject
派生的 class。我想将该信号连接到派生 class 的插槽。
class MSys : public QObject
{
Q_OBJECT
public:
explicit MSys(QObject *parent = 0) : QObject(parent) {}
virtual ~MSys() {}
public slots:
void onRequset();
};
class AbsView
{
protected:
AbsView() : m_sys(new MSys)
{
// QObject::connect(this, SIGNAL(request()), m_sys, SLOT(onRequset()));
/* What can I do here !? */
}
public:
virtual ~AbsView() {}
signals:
virtual void request() = 0;
private:
MSys *m_sys;
};
Q_DECLARE_INTERFACE(AbsView, "AbsView")
一旦构造函数完成,这就不是问题:然后 dynamic_cast<QObject*>(this)
将在任何接口的方法中工作。但是在构造函数中,这似乎是不可能的。
有什么办法吗?
一般来说,接口应该是抽象的 classes,它们的构造函数根本不应该做任何事情。所以这是糟糕的设计。
如果您坚持这样做,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
派生的 classes。它不适用于那些不适用的人。
另一种方法是延迟连接,直到事件循环有机会 运行。如果在此之前不需要连接,这是可以接受的。这不需要向 Interface
构造函数传递一个指向基 class.
计时器由当前线程的事件调度程序拥有,这样即使事件循环从未启动它也不会泄漏。
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 有意义,前提是它在 class 中 both:- 源自
QObject
,并且 - 包含
Q_OBJECT
宏。
- 源自
一般来说,虚拟信号没有什么意义。它们都是 "virtual",因为你可以在不给编译器一个虚拟方法的情况下连接事物。
最后,它们也不适用 Qt 5 编译时验证的语法,因为 Interface
不是具体的 QObject
-派生 class 并带有适当的信号