【问题标题】:QCoreApplication destructor is not called未调用 QCoreApplication 析构函数
【发布时间】:2014-04-25 12:34:59
【问题描述】:

我有一个从QCoreApplication 类派生的应用程序,并且有一个子线程成员。当我删除应用程序对象时,它有时会删除,有时不会。

class My_class :public QCoreApplication{
   private: 
   My_object* obj;
   QThread *th;
   public:
   My_class(){
      obj=new My_object();
      th= new QThread();
      obj->movetoThread(th);  // so it starts to run   
   }  
   ~My_class(){
      delete obj;       
      cout<<" App Destructor called"<<endl;
      }

   static void exit_(){quit();}        
 };

 // So in main I suddenly close my application and i want to exit and delete obj;
  int main()
  {
      My_class app;

      signal(SIGTERM,&app.exit_); 
      signal(SIGINT,&app.exit_); 
      signal(SIGBREAK,&app.exit_); 
      return app.exec();
  }
  // The obj destructor is;
  ~My_object::My_object(){cout<<"Object dest called"<<endl;}
  // The output of my program always writes "Object dest called"
  //But sometimes writes " App Destructor called".

所以我的程序总是进入 obj 的 destructpr,但有时它会进入 app 析构函数,有时不会。如何实现?

【问题讨论】:

  • 我认为它确实指出了发布完整、独立、可编译的代码是多么非常重要。这样我们就不必在这样的愚蠢错误上浪费时间了。
  • @KubaOber 是的,你是对的

标签: c++ c qt


【解决方案1】:

您可能有兴趣使用记录在 here 的应用程序销毁后例程。

在这种情况下,您可以注册一个 post 例程来清理您存储在给定集合中的所有线程:

 QList<QThread*> myThreads;
 static void cleanup_threads()
 {
     foreach (QThread thread, myThreads) {
         // cleanup the thread and delete it
     }
 }

 int main(int argc, char **argv) {
     QCoreApplication app(argc, argv);
     qAddPostRoutine(cleanup_threads);

     // setup signal handlers
     // create threads
     return app.exec();
 }

除非您确切知道自己在做什么,否则这种方法可能非常危险。首选方法是创建另一个派生自 QObject 的类,您可以将线程作为父级:

class MyThreadManager : public QObject {
    MyThreadManager(QObject *parent = 0) 
        : QObject(parent) 
    {
        for (int i = 0; i < 5; ++i) {
            MyThread *thread = new MyThread(this);
            // configure the thread object
            thread->start();

            // maybe add it to a local collection for later access: 
            // m_threads.append(thread);
        }
    }
};

int main(int argc, char **argv) {
    QCoreApplication app(argc, argv);
    // setup signal handlers
    MyThreadManager manager;
    return app.exec();
}

这样,您的所有线程都成为同一个对象的父级,该对象在销毁时由父级的 QObjectCleanupHandler 清理。

【讨论】:

  • 这是一种相当复杂的方法。
  • 请注意@KubaOber,我正在使用发布的原始代码。他似乎想要跟踪线程,以及与这些线程关联的对象。在助手类中维护它不一定是一个坏主意。话虽如此,我想我认为您对我的回复的评论是没有正当理由的否定,因此不是很有帮助
  • 原始代码的问题相当简单,不需要大量的设计更改。对象的竞争/不受保护的跨线程访问,仅此而已。
【解决方案2】:

您将obj 移动到另一个线程:

My_class(){
  obj=new My_object();
  th= new QThread();
  obj->movetoThread(th);  // so it starts to run   
}

一个QObject 只能从它的线程中删除,如果它有一个。因此,这段代码是错误的:

~My_class() {
   // You can't delete `obj` from a thread other than `th`!
   delete obj;       
   cout<<" App Destructor called"<<endl;
}

你必须做的是先完成线程,然后才删除对象:

~My_class() {
  th->quit();
  th->wait();
  delete th;
  Q_ASSERT(obj->thread() == NULL); // thus it's safe to delete obj now!
  delete obj;
}

这可以自动完成。我认为没有理由将线程和对象保留在堆上。这是一种过早的悲观情绪。如果您打算在第一次使用时延迟构造对象及其线程,那么您应该使用智能指针:QScopedPointer(或std::unique_ptr,但不是 std::auto_ptr)。

class Thread : public QThread {
  using QThread::run; // It's a final class
public:
  explicit Thread(QObject * parent = 0) : QThread(parent) {}
  ~Thread() { quit(); wait(); }
}

class My_Class : public QCoreApplication{
  My_Object m_obj;
  Thread m_thread; // Must be declared after any objects that live in it.
public:
  My_Class(){
    m_obj.moveToThread(&m_thread);
    m_thread.start(); 
  }  
  static void exit_() { quit(); }        
};

这是可行的,因为m_thread 将在m_obj 之前被破坏。您可以而且应该利用 C++ 编译器为您生成正确的代码。毕竟,编译器总是会做对的。

【讨论】:

    猜你喜欢
    • 2023-04-08
    • 2017-04-28
    • 2013-07-07
    • 2013-09-19
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 2011-04-16
    相关资源
    最近更新 更多