QTimer::SingleShot 删除对象后触发

QTimer::SingleShot fired after object is deleted

// 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() 吗?我遇到了一次极其罕见的崩溃,不确定是不是因为这个逻辑有问题。

编辑:这个答案是对原始问题的回应,该问题没有使用 QObjectclass A 作为一个独立的 class 没有继承任何东西。后来对该问题进行了编辑,使该答案已过时,但我将其保留在这里以显示如果不使用 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);

您没有义务在删除之前断开 object 的信号和插槽。

QObject 析构函数将为您清理过时的signal-slot 连接,只要您:

  1. 继承自QObject

  2. Use the Q_OBJECT macro in your class definition

遵循这些约定可确保您的 object 在删除时发出 destroyed() 信号。这实际上就是 Qt 的 signals-and-slots 系统用来清理悬挂引用的方法。

如果您想添加一些调试代码来跟踪 object 生命周期,您可以自己收听 destroyed() 信号。

(根据您使用的 Qt/moc 的特定版本,很可能带有 non-QObject 的代码使用插槽,或者 QObject-derived class 不使用插槽在其 header 中没有 Q_OBJECT 仍会编译,但会导致在运行时在垃圾指针上调用 timerSlot() 方法。)

I'm getting a extremely rare crash due to timer out of object scope which I need to fire just once. I use QTimer::singleShot which is static method and does not pertain to an instance of QTimer object which I would release with the context it fires the signal to.

这当然在 QTimer class 中得到解决,所需的行为由定时器 class 的实例控制,并设置了非静态 QTimer::singleShot 属性为真。

// 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);

因此,不会因计时器随上下文对象一起释放而发生崩溃。

即使计时器触发,也不会触发插槽。 ~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 的唯一方法是使用线程。

为了确定,您可以自己停止计时器:

class A : public QObject {
    QTimer t;
    A()  { connect(Signal-Slots); }
    ~A() { t.stop(); }
    fun() { t.start(10); }
    ...
};