QTimer::SingleShot 在对象被删除后触发
Posted
技术标签:
【中文标题】QTimer::SingleShot 在对象被删除后触发【英文标题】:QTimer::SingleShot fired after object is deleted 【发布时间】:2016-10-18 17:05:58 【问题描述】:// Example class
class A : public QObject
Q_OBJECT
void fun()
Timer::SingleShot(10, timerSlot); //rough code
public slot:
void timerSlot();
auto a = SharedPointer<A>(new A);
a->fun();
a->reset(); // a deleted
在这种情况下,在删除 a 并触发计时器后,它会执行 timerSlot()
吗?我遇到了极其罕见的崩溃,不确定是不是因为这个逻辑有问题。
【问题讨论】:
请删除“粗略代码”并在其中添加精确代码。在测试用例中,魔鬼在细节中...... 【参考方案1】:即使计时器触发,它也不会触发插槽。 ~QObject
状态的文档:All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue. 唯一可以同时触发A::timerSlot
和删除A
的方法是使用线程。
【讨论】:
当 OP 发布问题时,QObject
不存在。现在它是。考虑到新的要求,我喜欢这个答案。
您写“唯一可以同时触发 A::timerSlot 和删除 A 的方法是使用线程。”。你能证明一下吗? “同时”是什么意思?你的意思是他是否利用了数据竞赛?
@JohannesSchaub-litb 没错。这是糟糕的设计,当然不应该这样做,但这是有可能的。【参考方案2】:
您没有义务在删除对象之前断开它的信号和插槽。
QObject 析构函数将为您清理过时的信号槽连接,只要您:
从 QObject 继承
Use the Q_OBJECT macro in your class definition
遵循这些约定可确保您的对象在删除时发出destroyed()
信号。这实际上就是 Qt 的信号槽系统用来清理悬空引用的方法。
如果您想添加一些调试代码来跟踪对象生命周期,您可以自己收听destroyed()
信号。
(根据您使用的 Qt/moc 的特定版本,使用插槽的非 QObject 的代码或在其标头中没有 Q_OBJECT 的 QObject 派生类的代码很可能仍然可以编译,但会导致timerSlot()
方法将在运行时在垃圾指针上调用。)
【讨论】:
是的,它继承自 QObject【参考方案3】:由于计时器超出对象范围,我遇到了极其罕见的崩溃,我只需要触发一次。我使用 QTimer::singleShot,它是静态方法,与 QTimer 对象的实例无关,我将在它触发信号的上下文中释放它。
这当然可以在QTimer
类中解决,并且期望的行为由计时器类的实例控制,其中非静态QTimer::singleShot 属性设置为true。
// declaration
QScopedPointer<QTimer> m_timer;
protected slots:
void onTimeout();
// usage
m_timer.reset(new QTimer);
m_timer->setSingleShot(true);
QObject::connect(m_timer.data(), SIGNAL(timeout()), this, SLOT(onTimeout()));
m_timer->start(requiredTimeout);
因此,不会因为与上下文对象一起释放的计时器而发生崩溃。
【讨论】:
【参考方案4】:编辑:此答案是对未使用 QObject
但将 class A
作为不继承任何内容的独立类的原始问题的回应。后来对该问题进行了编辑,使该答案已过时,但我将把它留在这里以说明如果不使用 QObject
会需要什么。
您可以做到这一点的唯一方法是让对象保持活动状态,直到计时器触发。例如:
class A : enable_shared_from_this<A>
void fun()
QTimer::singleShot(10, bind(&A::timerSlot, shared_from_this()));
public:
void timerSlot();
auto a = SharedPointer<A>(new A);
a->fun();
a->reset(); // a goes out of scope, but its referent is kept alive by the `QTimer`.
上述工作的原因是您在设置计时器时将 shared_ptr 捕获到class A
,并且计时器将保持它(否则它无法触发)。
如果您不喜欢或不能使用最新的 C++ 功能或 Boost:
struct Functor
Functor(SharedPointer<A> a) : _a(a)
void operator() a->timerSlot();
SharedPointer _a;
;
class A
void fun(shared_ptr<A> self)
QTimer::singleShot(10, Functor(self));
public:
void timerSlot();
auto a = SharedPointer<A>(new A);
a->fun(a);
【讨论】:
所以,主要问题是,如果a
超出范围,计时器会触发(根据我的代码库)吗?还是 QTimer 会失效?
除非您销毁或禁用计时器,否则计时器将触发。当它触发时,在您的原始代码中,将导致未定义的行为,因为 this
指针将无效(释放后使用)。
请注意:您的“没有最近的 C++ 功能”部分使用 std::shared_ptr
,它是在 C++11 中引入的。
@JonHarper:谢谢。这是一个简单的错字。现已修复(使用 OP 拥有的 SharedPtr)。【参考方案5】:
为了确定,您可以自己停止计时器:
class A : public QObject
QTimer t;
A() connect(Signal-Slots);
~A() t.stop();
fun() t.start(10);
...
;
【讨论】:
我问的不是这个 @NullPointer。如果你以这种方式实现它,你的问题是没有意义的! @NullPointer:你的问题报告不仅包含问题,还包含崩溃报告,这是编程艺术中最不合格的结果。因此我假设,您的突出目标是避免您的程序崩溃,并为您提供一个程序设计,避免可疑的崩溃原因。将我的建议与亚历山大的建议进行比较,并做出最确定的综合。以上是关于QTimer::SingleShot 在对象被删除后触发的主要内容,如果未能解决你的问题,请参考以下文章
QTimer::singleShot(0, object SLOT(obj_slot())) 做啥?
QTimer::singleShot 仅在间隔为 0 时调用 lambda
PyQT实时显示串口数据在QtCore.QTimer.singleShot()上抛出最大递归深度超出异常
当 singleShot 为 True 时,QTimer 永远不会触发超时 [关闭]
Qt中使用定时器(可使用QObject::timerEvent定时执行,QTimer::singleShot可只触发一次)