Search code examples
c++qtsignals-slotsqt5

How to delete tied objects in Qt?


There are two classes: Widget and Worker. Here is a schematic code.

class Widget
{
    Worker *m_worker;
    QTextEdit *m_edit;
public:

    Widget():m_edit(new QTextEdit){}

   ~Widget()
   {
       m_worker->ShouldStop = true;
       delete *m_worker;
   }

   void doWork()
   {
      m_worker = new Worker;

      if (!worker->doWork())
          m_edit->setText("failed");
   }
}

class Worker
{

    Worker() : ShouldStop(false){}

public:
    bool ShouldStop;

    bool doWork()
    {
        while(true && !ShouldStop)
        {
            QThread::sleep(1);
            QApplication::processEvents();
        }

        //consider the work undone if a stop was forced
        if (ShouldStop)
            return false;
    }
}

After a call to doWork() of the Widget the execution loops in the method doWork() of the Worker. A widget is then closed and its destructor is called during one of the calls to processEvents(). Then the execution then returns to the doWork() of the Worker. It now checks ShouldStop and returns to the doWork() of the Widget and tries to add something to the m_edit. However the Widget object is already dead.

Questions:

  1. How can a Worker be deleted cleanly?
  2. What is the best design to avoid such an interplay?

Solution

  • Ideally, worker threads should just return data via the signals and slots mechanism, not directly accessing the original object. Actually creating a thread and avoiding the call to QApplication::processEvents() is the first way to avoid the problem.

    Additionally, you should consider using a design like this when you want to start a worker in a new thread. Rather than subclassing QThread, you can just create a generic QThread and assign a QObject to it, like so:

    Worker *worker = new Worker;
    QThread *workerThread = new QThread(this);
    
    connect(workerThread, &QThread::started, worker, &Worker::doWork);
    connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);
    worker->moveToThread(workerThread);
    
    // Starts an event loop, and emits workerThread->started()
    workerThread->start();
    

    Consider restructuring your code around this pattern if you're using Qt 5.