以下是一个完整的Qt代码示例,展示了未使用 deleteLater
而直接使用 delete
导致问题的情况,该示例涉及到一个简单的多线程场景,主线程创建一个工作线程,工作线程完成任务后通知主线程,在对象删除处理不当的情况下会出现崩溃等问题。
示例代码
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QCoreApplication>// 工作类,继承自QObject,用于在工作线程中执行任务
class Worker : public QObject
{Q_OBJECT
public:Worker() {}signals:// 任务完成后发送信号,传递一个整数值给主线程void resultReady(int result);public slots:void doWork(){// 模拟耗时任务,这里简单睡眠一下QThread::sleep(2);int result = 42;// 发送任务完成的信号emit resultReady(result);}
};// 控制器类,用于管理工作线程和处理结果
class Controller : public QObject
{Q_OBJECT
public:Controller(){// 创建工作对象和线程对象worker = new Worker();thread = new QThread();// 将工作对象移动到工作线程中worker->moveToThread(thread);// 连接线程启动信号和工作对象的工作函数connect(thread, &QThread::started, worker, &Worker::doWork);// 连接工作对象的完成信号和自身的处理结果函数connect(worker, &Worker::resultReady, this, &Controller::handleResult);// 启动线程thread->start();}~Controller(){// 这里直接delete工作对象,会导致问题,应该使用worker->deleteLater()delete worker;thread->quit();thread->wait();delete thread;}private slots:void handleResult(int result){qDebug() << "Received result: " << result;}private:Worker *worker;QThread *thread;
};int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);{Controller controller;}return app.exec();
}#include "main.moc"
问题分析
在上述代码中,Controller
类负责管理工作线程和处理工作线程返回的结果。在 Controller
的析构函数中,直接使用 delete
来删除 worker
对象存在以下严重问题:
-
对象生命周期冲突:
当Controller
的析构函数被调用时(例如在main
函数中Controller
实例所在的作用域结束时),如果此时工作线程中的worker
对象还在执行doWork
函数(比如还处于QThread::sleep(2)
的睡眠阶段或者刚结束睡眠正准备发送resultReady
信号),直接调用delete
去删除worker
对象,就违背了对象的正常生命周期。因为worker
对象在工作线程中仍处于活动状态,其内部资源还在被使用,这样强制删除会导致工作线程后续可能对已经释放的内存进行访问(例如尝试去发送resultReady
信号时,相关的对象内存已经不存在了),从而引发程序崩溃,一般会出现类似“段错误”的报错提示。 -
线程安全问题:
由于是在多线程环境下,主线程(也就是Controller
所在的线程,执行析构函数的线程)直接删除了worker
对象,而工作线程并不知道这个情况,这就造成了线程间的同步问题。如果工作线程后续继续按照正常流程去操作worker
对象(比如触发信号等),就会出现对非法内存区域的访问,破坏了多线程编程中对共享对象访问的安全性要求,导致程序出现未定义行为甚至崩溃。
正确的做法应该是在合适的时机调用 worker->deleteLater()
,例如可以在 handleResult
槽函数中判断任务已经彻底完成,不会再有对 worker
对象的操作后,调用 worker->deleteLater()
来安全地安排 worker
对象的删除,让Qt的事件循环在确保安全的情况下(比如工作线程相关的所有操作都完成后)再执行删除操作,避免上述出现的各种问题,保障程序的稳定运行。
希望这个示例能够清晰地展示未合理使用 deleteLater
所带来的问题,你如果还有其他疑问或者想进一步了解相关知识,可以随时问我。