上下文对象用于两种场景。
自动断开
让我们先退一步问问自己:Qt 什么时候断开连接?
用通常的connect(sender, signal, receiver, slot)连接,有三种可能:
- 当有人明确拨打
disconnect时;
- 当
sender被删除时;
-
receiver 被删除时。
特别是在 #2 和 #3 的情况下,Qt 以这种方式运行才有意义(实际上,它必须以这种方式运行,否则您会遇到资源泄漏和/或崩溃) .
现在:当使用 connect 重载获取函子时,Qt 何时断开连接?
请注意,没有context 参数,只涉及一个QObject:发送者。因此答案是:
- 当有人明确呼叫
disconnect时;
-
sender 被删除时。
当然,这里没有接收器对象!所以只有发送者会自动控制连接的生命周期。
现在,问题是函子可能会捕获一些可能变得无效的额外状态,在这种情况下,连接会自动断开。典型情况是使用 lambda:
connect(sender, &Sender::signal,
[&object1, &object2](Param p)
{
use(object1, object2, p);
}
);
如果object1 或object2 被删除会怎样?连接仍然是活动的,因此发出信号仍然会调用 lambda,而后者又会访问被破坏的对象。这有点糟糕......
因此,当涉及到仿函数时,引入了一个采用 上下文对象 的 connect 重载。使用该重载建立的连接也将自动断开
-
context 对象被删除时。
你可能是对的,例如,你会在很多次看到仿函数中使用的“主”对象
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 在尝试访问它们之前。
在特定线程/事件循环中运行仿函数
很快:指定上下文对象时,仿函数将运行到上下文的线程中,就像使用接收器对象的正常连接一样。确实,请注意采用上下文的connect 重载也采用连接类型(而没有上下文的则不采用连接类型——连接始终是直接的)。
同样,这很有用,因为 QObject 不是可重入的或线程安全的,并且您必须仅在它所在的线程中使用 QObject。如果您的仿函数访问另一个线程中的对象,它必须在该线程中执行;将该对象指定为上下文可以解决问题。