C++中如果要在堆内存中创建和销毁对象需要借助关键字new和delete来完成, new操作符用于创建对象, delete操作符用于销毁对象。
虽然随着计算机硬件的不断更新迭代, 内存越来越大, 但是对于一个计算机应用, 系统分配的内存是有限的, 而且”一旦越线“可能就会被系统强行kill, 所以内存还是弥足珍贵的。
合理的利用”delete“可以有效减少应用对内存的消耗。但是delete的不合理使用常常导致应用crash。而”deleteLater()“可以更好的规避风险, 降低崩溃。
一、 ”deleteLater()“的使用
下面是官方文档对 ”deleteLater()“的介绍
Schedules this object for deletion. The object will be deleted when control returns to the event loop. If the event loop is not running when this function is called (e.g. deleteLater() is called on an object before QCoreApplication::exec()), the object will be deleted once the event loop is started. If deleteLater() is called after the main event loop has stopped, the object will not be deleted. Since Qt 4.8, if deleteLater() is called on an object that lives in a thread with no running event loop, the object will be destroyed when the thread finishes. Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() was called. This does not apply to objects deleted while a previous, nested event loop was still running: the Qt event loop will delete those objects as soon as the new nested event loop starts. Note: It is safe to call this function more than once; when the first deferred deletion event is delivered, any pending events for the object are removed from the event queue. Note: This function is thread-safe.
总结一下就是以下几点:
- 首先”deleteLater()“是QObject对象的一个函数, 要想使用此方法, 必须是一个QObject对象。
- ”deleteLater()“依赖于Qt的event loop机制。
- 如果在event loop启用前被调用, 那么event loop启用后对象才会被销毁;
- 如果在event loop结束后被调用, 那么对象不会被销毁;
- 如果在没有event loop的thread使用, 那么thread结束后销毁对象。
- 可以多次调用此函数。
- 线程安全。
二、”deleteLater()“的原理
- 函数被调用后, 会向QCoreApplication发送一个QDeferredDeleteEvent
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
...
bool QObject::event(QEvent *e)
{
switch (e->type()) {
....
case QEvent::DeferredDelete:
qDeleteInEventHandler(this);
break;
....
}
}
...
void qDeleteInEventHandler(QObject *o)
{
delete o;
}
- 此对象继续运行, 直到收到QDeferredDeleteEvent
- 收到QDeferredDeleteEvent后, 此对象将从event loop 中被移除
- 对象被销毁
三、验证案例
#include <QApplication>
#include <QObject>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
QString currentStrTime ()
{
return QDateTime::currentDateTime().toString("hh:mm:ss");
}
using namespace std;
class DemoClass : public QObject
{
public:
explicit DemoClass(QString key) : _key(move(key)) {
qInfo() << currentStrTime() << _key << " => created" ;
}
~DemoClass() override {
qInfo() << currentStrTime() << _key << " => deleted" ;
}
protected:
bool event(QEvent* e) override {
if(e->type() == QEvent::DeferredDelete)
qInfo() << currentStrTime() << _key << " => receive QDeferredDeleteEvent" ;
return QObject::event(e);
}
private:
QString _key{};
};
int main(int argc, char** argv)
{
// 创建 beforeEventLoop 对象 并在 EventLoop 开始前调用deleteLater()方法
auto* beforeEventLoop = new DemoClass("before event loop demo");
beforeEventLoop->deleteLater();
// 创建 normalDemo 在运行2秒后调用deleteLater()方法
auto* normalDemo = new DemoClass("normal demo");
// 创建 afterEventLoop 在 EventLoop 结束后调用deleteLater()方法
auto* afterEventLoop = new DemoClass("after event loop demo");
// 创建QApplication对象
QApplication a(argc, argv);
// 销毁 normalDemo 定时器
QTimer::singleShot(2000, [normalDemo](){
// 多次调用
normalDemo->deleteLater();
normalDemo->deleteLater();
normalDemo->deleteLater();
});
// 系统退出定时器
QTimer::singleShot(3000, [](){
QApplication::exit();
});
// 启动 EventLoop
qInfo() << currentStrTime() << "start event loop" ;
a.exec();
// 退出 EventLoop
qInfo() << currentStrTime() << "event loop stoped" ;
// 调用 afterEventLoop 的 deleteLater()方法
afterEventLoop->deleteLater();
return 0;
}
运行结果如下:
"13:51:36" "before event loop demo" => created
"13:51:36" "normal demo" => created
"13:51:36" "after event loop demo" => created
"13:51:36" start event loop
"13:51:36" "before event loop demo" => receive QDeferredDeleteEvent
"13:51:36" "before event loop demo" => deleted
"13:51:38" "normal demo" => receive QDeferredDeleteEvent
"13:51:38" "normal demo" => deleted
"13:51:39" event loop stoped