QObject::connect 函数中的 QObject* 上下文
QObject* context in QObject::connect function
我已经阅读了 QObject::connect 的 documentation(针对 Qt 5.4),但我对重载有疑问
QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
context
参数到底是什么?它的目的是什么?它可以用于在线程中的本地事件循环中建立连接吗?
有人可以提供 how/when 的示例来使用此重载(当上下文不是 this
时)吗?
上下文对象在两种情况下使用。
自动断开连接
让我们先退一步问问自己:Qt 什么时候断开连接?
用通常的connect(sender, signal, receiver, slot)
连接,有三种可能:
- 当有人明确调用
disconnect
;
- 当
sender
被删除时;
- 删除
receiver
时
特别是在 #2 和 #3 的情况下,Qt 这样做是有意义的(实际上,它 必须 那样做,否则你会发生资源泄漏and/or 崩溃)。
现在:当使用 connect
重载仿函数时,Qt 什么时候断开连接?
请注意,如果没有 context
参数,则只涉及一个 QObject:发送者。因此答案是:
- 当有人明确调用
disconnect
;
- 删除
sender
时。
当然,这里没有receiver对象!所以只有发件人自动控制连接的生命周期。
现在,问题是仿函数可能会捕获一些可能变得无效的额外状态,在这种情况下希望连接自动中断。典型的情况是 lambdas:
connect(sender, &Sender::signal,
[&object1, &object2](Param p)
{
use(object1, object2, p);
}
);
如果 object1
或 object2
被删除会怎样?连接仍将存在,因此发出信号仍将调用 lambda,后者将访问已销毁的对象。这有点糟糕...
出于这个原因,当涉及到仿函数时,引入了采用 上下文对象 的 connect
重载。使用该过载建立的连接也将自动断开
- 删除
context
对象时。
您可能是对的,您会多次看到仿函数中使用的完全相同的 "main" 对象,例如
connect(button,
&QPushButton::clicked,
otherWidget,
[otherWidget]()
{
otherWidget->doThis(); otherWidget->doThat();
}
);
这只是 Qt 中的一种模式——在为子对象设置连接时,您通常将它们连接到 this
对象上的插槽,因此 this
可能是最常见的上下文。但是,一般来说,您也可能会得到类似
的结果
// manages the lifetime of the resources; they will never outlive this object
struct ResourceManager : QObject
{
Resource res1; // non-QObjects
OtherResource res2;
};
ResourceManager manager;
connect(sender, signal, manager, [&manager](){ use(manager.res1, ...); });
// or, directly capture the resources, not the handle
因此,您正在捕获 manager
的部分状态。
在最一般的情况下,当没有context
对象可用时,如果lambda捕获的对象有可能在连接中存活下来,那么你必须通过弱指针捕获它们,并尝试锁定在尝试访问它们之前在 lambda 中的那些指针。
运行 特定 thread/event 循环中的仿函数
很快:当指定上下文对象时,仿函数将运行进入上下文的线程,就像使用接收者对象的正常连接一样。实际上,请注意 connect
重载采用上下文 也 采用连接类型(而没有上下文的重载不采用连接类型 - 连接始终是直接的)。
同样,这很有用,因为 QObject 不是可重入的或线程安全的,您必须仅在它所在的线程中使用 QObject。如果您的仿函数访问另一个线程中的对象,则它必须在该线程中执行线;将该对象指定为上下文可以解决问题。
我已经阅读了 QObject::connect 的 documentation(针对 Qt 5.4),但我对重载有疑问
QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
context
参数到底是什么?它的目的是什么?它可以用于在线程中的本地事件循环中建立连接吗?
有人可以提供 how/when 的示例来使用此重载(当上下文不是 this
时)吗?
上下文对象在两种情况下使用。
自动断开连接
让我们先退一步问问自己:Qt 什么时候断开连接?
用通常的connect(sender, signal, receiver, slot)
连接,有三种可能:
- 当有人明确调用
disconnect
; - 当
sender
被删除时; - 删除
receiver
时
特别是在 #2 和 #3 的情况下,Qt 这样做是有意义的(实际上,它 必须 那样做,否则你会发生资源泄漏and/or 崩溃)。
现在:当使用 connect
重载仿函数时,Qt 什么时候断开连接?
请注意,如果没有 context
参数,则只涉及一个 QObject:发送者。因此答案是:
- 当有人明确调用
disconnect
; - 删除
sender
时。
当然,这里没有receiver对象!所以只有发件人自动控制连接的生命周期。
现在,问题是仿函数可能会捕获一些可能变得无效的额外状态,在这种情况下希望连接自动中断。典型的情况是 lambdas:
connect(sender, &Sender::signal,
[&object1, &object2](Param p)
{
use(object1, object2, p);
}
);
如果 object1
或 object2
被删除会怎样?连接仍将存在,因此发出信号仍将调用 lambda,后者将访问已销毁的对象。这有点糟糕...
出于这个原因,当涉及到仿函数时,引入了采用 上下文对象 的 connect
重载。使用该过载建立的连接也将自动断开
- 删除
context
对象时。
您可能是对的,您会多次看到仿函数中使用的完全相同的 "main" 对象,例如
connect(button,
&QPushButton::clicked,
otherWidget,
[otherWidget]()
{
otherWidget->doThis(); otherWidget->doThat();
}
);
这只是 Qt 中的一种模式——在为子对象设置连接时,您通常将它们连接到 this
对象上的插槽,因此 this
可能是最常见的上下文。但是,一般来说,您也可能会得到类似
// manages the lifetime of the resources; they will never outlive this object
struct ResourceManager : QObject
{
Resource res1; // non-QObjects
OtherResource res2;
};
ResourceManager manager;
connect(sender, signal, manager, [&manager](){ use(manager.res1, ...); });
// or, directly capture the resources, not the handle
因此,您正在捕获 manager
的部分状态。
在最一般的情况下,当没有context
对象可用时,如果lambda捕获的对象有可能在连接中存活下来,那么你必须通过弱指针捕获它们,并尝试锁定在尝试访问它们之前在 lambda 中的那些指针。
运行 特定 thread/event 循环中的仿函数
很快:当指定上下文对象时,仿函数将运行进入上下文的线程,就像使用接收者对象的正常连接一样。实际上,请注意 connect
重载采用上下文 也 采用连接类型(而没有上下文的重载不采用连接类型 - 连接始终是直接的)。
同样,这很有用,因为 QObject 不是可重入的或线程安全的,您必须仅在它所在的线程中使用 QObject。如果您的仿函数访问另一个线程中的对象,则它必须在该线程中执行线;将该对象指定为上下文可以解决问题。