如何在Qt中删除绑定的对象?

时间:2013-03-05 23:35:54

标签: c++ qt signals-slots qt5

有两个类:WidgetWorker。这是一个示意图代码。

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;
    }
}

doWork()的方法Widget中调用doWork() Worker执行循环后。然后关闭窗口小部件,并在对processEvents()的一次调用期间调用其析构函数。然后执行返回doWork()的{​​{1}}。 它现在会检查Worker并返回ShouldStop的{​​{1}}并尝试向doWork()添加内容。但是Widget对象已经死了。

问题:

  1. 如何干净地删除工人?
  2. 避免这种相互作用的最佳设计是什么?

3 个答案:

答案 0 :(得分:3)

理想情况下,工作线程应该只通过信号和插槽机制返回数据,而不是直接访问原始对象。实际上创建一个线程并避免调用QApplication::processEvents()是避免这个问题的第一种方法。

此外,当您想要在新线程中启动worker时,应考虑使用这样的设计。您可以创建一个通用QThread并为其分配QObject,而不是子类化QThread,如下所示:

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

如果您使用Qt 5,请考虑围绕此模式重构代码。

答案 1 :(得分:2)

@Alex答案绝对是q2的方法。他只是忘了提到如何进行删除(q1)。假设Widget创建了对象workerThread,那么这是必要的:

 ~Widget(){
   if(workerThread->isRunning()){
         workerThread->terminate(); <--- this line, right there
         workerThread->wait(): 
   }
 ...
 }

为什么呢?因为删除QThread只是删除线程对象but doesn't stop the thread from running, and may crash the application。请注意,如果您正在使用

connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);

如Alex提到的那样,那么在调用worker之后访问terminate是不安全的,因为它会触发finished()信号,执行worker->deleteLater,最终删除worker

答案 2 :(得分:1)

你可以让Widget使用一个信号来通知Widget的所有者为Widget启动Worker。

所有者拥有Widget和Worker。 Widget告诉所有者触发了某些事情,并且所有者负责决定Worker的doWork()是正确的响应,以及管理Worker和Widget的生命周期。工人和小工具彼此无知,但所有者可能会将他们的一些信号和插槽连接在一起。