6
// 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

In this case after a is deleted and timer is fired, would it execute timerSlot()? I'm getting an extremely rare crash and not sure if it's because of something fishy in this logic.

Patrick
  • 1,717
  • 7
  • 21
  • 28
JamesWebbTelescopeAlien
  • 3,547
  • 2
  • 30
  • 51
  • Please remove "rough code" and add the *exact* code in there. In a testcase the devil is in the details... – peppe Jun 19 '16 at 17:02

5 Answers5

9

Even if the timer fires, it won't trigger the slot. The docs of ~QObject state: All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue. The only way you can trigger the A::timerSlot and delete A at the same time is if you use threads.

thuga
  • 12,601
  • 42
  • 52
  • When OP posted the question, `QObject` was not there. Now it is. I like this answer given the new requirements. – John Zwinck Jun 18 '16 at 12:29
  • You write "The only way you can trigger the A::timerSlotand delete A at the same time is if you use threads.". Can you please demonstrate that? What do you mean by "at the same time"? Do you mean if he exploits data races? – Johannes Schaub - litb Jun 18 '16 at 12:30
  • @JohannesSchaub-litb Exactly. It's bad design and shouldn't be done of course, but it's a possibility. – thuga Jun 20 '16 at 06:43
2

You are not obligated to disconnect an object's signals and slots before deleting it.

The QObject destructor will clean up obsolete signal-slot connection for you, as long as you:

  1. Inherit from QObject

  2. Use the Q_OBJECT macro in your class definition

Following these conventions ensures that your object emits a destroyed() signal when deleted. That's actually what Qt's signals-and-slots system uses to clean up dangling references.

You can listen to the destroyed() signal yourself if you'd like to add some debugging code to track object lifecycles.

(Depending on the particular version of Qt/moc you are using, it's quite possible that code with a non-QObject using slots, or a QObject-derived class that doesn't have Q_OBJECT in its header will still compile but cause the timerSlot() method to be invoked on a garbage pointer at runtime.)

Alex P
  • 1,559
  • 11
  • 23
0

Edit: This answer was in response to the original question which did not use QObject but had class A as a standalone class inheriting nothing. The question was later edited making this answer obsolete, but I'll leave it here to show what would be needed if not using QObject.


The only way you can do that is if you keep the object alive until the timer has fired. For example:

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`.

The reason the above works is that you capture a shared_ptr to class A when setting the timer, and the timer will hold onto it (else it can't fire).

If you don't like or can't use recent C++ features or 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);
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • So, main question, would the timer fire (as per my code base) if `a` goes out of scope? or QTimer would be invalidated? – JamesWebbTelescopeAlien Jun 17 '16 at 01:56
  • The timer would fire unless you destroy or disable the timer. And when it fires, in your original code, undefined behavior would result, because the `this` pointer would be invalid (use-after-free). – John Zwinck Jun 17 '16 at 03:14
  • Just a note: your "without recent C++ features" section uses `std::shared_ptr`, which was introduced in C++11. – jonspaceharper Jun 18 '16 at 00:18
  • @JonHarper: Thanks. It was a simple typo. Fixed now (using SharedPtr which OP has). – John Zwinck Jun 18 '16 at 12:28
0

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.

That is of course solved in QTimer class and desired behavior controlled by the instance of timer class with non-static QTimer::singleShot property set to 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);

So, no crash should happen due to timer released with the context object.

Alexander V
  • 8,351
  • 4
  • 38
  • 47
-1

To reach certainty, you can stop the timer yourself:

class A : public QObject {
    QTimer t;
    A()  { connect(Signal-Slots); }
    ~A() { t.stop(); }
    fun() { t.start(10); }
    ...
};
Patrick
  • 1,717
  • 7
  • 21
  • 28
Roland
  • 336
  • 2
  • 8
  • Thats not what I asked – JamesWebbTelescopeAlien Jun 18 '16 at 23:30
  • @NullPointer. if You implement it this way, Your question is pointless! – Roland Jun 20 '16 at 15:14
  • @NullPointer: your problem report contains not only a question, but also a crash report, which is the most disqualifying result in programming art. Therefor I assumed, that your prominent goal is to avoid your program crashing, and offered you a program design, which avoid the suspect crash cause. Compare my with the proposal of Alexander and make most certain synthesis. – Roland Jun 21 '16 at 07:05