未调用 QCoreApplication 析构函数

Posted

技术标签:

【中文标题】未调用 QCoreApplication 析构函数【英文标题】:QCoreApplication destructor is not called 【发布时间】: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 是的,你是对的 【参考方案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++ 编译器为您生成正确的代码。毕竟,编译器总是会做对的。

【讨论】:

以上是关于未调用 QCoreApplication 析构函数的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 C++ 中第二次调用析构函数未定义的行为?

如果调用 asio::strand 的析构函数时,该链上仍有一些就绪/未就绪的处理程序怎么办?

在 Visual Studio 中,与 std::async 一起使用时未调用“thread_local”变量的析构函数,这是一个错误吗?

我可以通过引用调用placement-new和析构函数吗?

在调用析构函数之前对象的生命周期已经结束?

Virtual destruct(虚析构函数)