核心思想是创建一个包装器,一个特殊的“连接”,它会自动断开信号连接。如果您使用了大量的“仅调用一次”连接,则这非常有用;否则,我建议在槽函数的开头使用Qobject :: disconnect。
此实现通过创建两个连接来工作:一个是“普通”的连接,另一个是连接并在之后断开和清理所有内容。
一种实现方式(使用C++11/Qt 5):
template <typename Func1, typename Func2>
static inline QMetaObject::Connection weakConnect(
typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete](){
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
注意事项/需要改进的地方:
- 清理操作发生在调用常规槽之后。如果常规槽导致信号再次被发射,常规槽将再次执行(可能导致无限递归)。
- 没有适当的断开连接方式,除非通过发射信号进行断开连接(可以使用
QObject::disconnect
,但这会导致小型内存泄漏)。
- 依赖于槽的执行顺序。目前看起来没问题。
- 命名问题。
测试使用:
class A : public QObject
{
Q_OBJECT
signals:
void sig(int a);
};
class B : public QObject
{
Q_OBJECT
public:
B(int b) : QObject(), b_(b) {}
int b() const { return b_; }
public slots:
void slo(int a) { qDebug() << "\tB :" << b_ << "a:" << a; }
private:
int b_;
};
和
A a1;
A a2;
B b10(10);
B b20(20);
weakConnect(&a1, &A::sig, &b10, &B::slo);
weakConnect(&a1, &A::sig, &b20, &B::slo);
weakConnect(&a2, &A::sig, &b20, &B::slo);
qDebug() << "a1 :"; emit a1.sig(1);
qDebug() << "a2 :"; emit a2.sig(2);
qDebug() << "a1 :"; emit a1.sig(3);
qDebug() << "a2 :"; emit a2.sig(4);
测试代码输出:
a1 :
B : 10 a: 1
B : 20 a: 1
a2 :
B : 20 a: 2
a1 :
a2 :
摆脱C++11/Qt5(我没有Qt 4.8/GCC4.4.7,所以未经过这些测试)根据文档,Qt 4.8没有connect to function,因此我正在使用一个包装器:
class ConnectJanitor : public QObject
{
Q_OBJECT
public slots:
void cleanup()
{
QObject::disconnect(conn_normal_);
QObject::disconnect(*conn_delete_);
delete conn_delete_;
delete this;
}
public:
static ConnectJanitor* make(QMetaObject::Connection conn_normal,
QMetaObject::Connection* conn_delete)
{
return new ConnectJanitor(conn_normal, conn_delete);
}
private:
ConnectJanitor(QMetaObject::Connection conn_normal,
QMetaObject::Connection* conn_delete) :
QObject(0) , conn_normal_(conn_normal), conn_delete_(conn_delete) {}
ConnectJanitor(const ConnectJanitor&);
ConnectJanitor& operator=(ConnectJanitor const&);
QMetaObject::Connection conn_normal_;
QMetaObject::Connection* conn_delete_;
};
我将ConnectJanitor
的构造函数设置为私有,因为该实例自毁(delete this
)
对于weakConnect
:
static inline QMetaObject::Connection weakConnect(const QObject * sender, const char * signal, const QObject * receiver, const char * slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, ConnectJanitor::make(conn_normal, conn_delete), SLOT(cleanup()));
return conn_normal;
}
如果您需要手动断开连接,我建议将weakConnect()返回ConnectJanitor的指针。
disconnect()
函数有什么问题? - vahanchodisconnect
呢?是有特定的原因吗? - Robert