【问题标题】:How to delete tied objects in Qt?如何在 Qt 中删除绑定的对象?
【发布时间】:2013-03-05 23:35:54
【问题描述】:

有两个类: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;
    }
}

在调用WidgetdoWork() 之后,在Worker 的方法doWork() 中执行循环。然后关闭一个小部件并在调用processEvents() 期间调用它的析构函数。然后执行然后返回到WorkerdoWork()。 它现在检查ShouldStop 并返回到WidgetdoWork() 并尝试向m_edit 添加一些内容。但是Widget 对象已经死了。

问题:

  1. 如何彻底删除 Worker?
  2. 避免这种相互作用的最佳设计是什么?

【问题讨论】:

    标签: c++ qt signals-slots qt5


    【解决方案1】:

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

    另外,当你想在一个新线程中启动一个工作线程时,你应该考虑使用这样的设计。与其继承 QThread,不如创建一个通用的 QThread 并为其分配一个 QObject,如下所示:

    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,请考虑围绕此模式重构您的代码。

    【讨论】:

    • 谢谢,但是您的回答中具体的 Qt5 是什么?
    • 我认为它适用于 4.8+。我只是没有测试过,而且我相当确定我的信号和插槽语法是 Qt5 特定的。
    【解决方案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, &amp;QThread::finished, worker, &amp;Worker::deleteLater);

    正如Alex所说,那么在调用terminate之后访问worker是不安全的,因为它会触发finished()信号,执行worker-&gt;deleteLater,最终删除worker

    【讨论】:

      【解决方案3】:

      您可以让 Widget 使用信号通知 Widget 的所有者为 Widget 启动 Worker。

      Owner 同时拥有 Widget 和 Worker。 Widget 告诉 Owner 触发了某些事情,而 Owner 负责确定 Worker 的 doWork() 是正确的响应,并负责管理 Worker 和 Widget 的生命周期。 Worker 和 Widget 互不了解,但 Owner 可能会将它们的一些信号和插槽连接在一起。

      【讨论】:

        猜你喜欢
        • 2015-09-02
        • 1970-01-01
        • 2015-07-04
        • 2012-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-24
        相关资源
        最近更新 更多