大家早上好
我将QSharedPointer
的类与QObject
一起使用。由于它们使用信号/插槽机制,因此必须使用QObject::deleteLater()
才能正确销毁它们,例如:
~QObject():“在等待传递待处理事件时删除QObject可能会导致崩溃。如果QObject存在于与当前正在执行的线程不同的线程中,则不能直接删除它。请使用deleteLater()相反,这将导致事件循环在所有未决事件传递给对象后删除该对象。”
QSharedPointer and QObject::deleteLater
QSharedPointer(X *ptr, Deleter d):“删除器参数d指定该对象的自定义删除器。当强引用计数下降到0时,将调用自定义删除器,而不是运算符delete()。实例,用于在QObject上调用deleteLater()代替”
还请注意,在previous link中是这样写的
“请注意,即使QSharedPointer模板参数T不同,也会使用指向X的指针来调用自定义删除器函数。”,
但这与构造函数QSharedPointer(X *ptr)说的完全不同:
“从Qt 5.8开始,当销毁对此QSharedPointer的最后一个引用时,将通过调用X的析构函数来删除ptr(即使X与QSharedPointer的模板参数T不同)。以前,曾调用过T的析构函数。 ” -(我正在使用Qt 5.7,所以我希望使用~T
析构函数)
好吧,最后,我想实现的是使用QSharedPointer
来调用(子类的)适当的析构函数,但是由于它是QObject
,因此我需要使用{{1 }},但在测试中我无法实现自己的目标。
我发布了一个简单的测试和结果。
您能告诉我我做错了什么吗吗吗?
我对测试的期望正确吗?
我对标有“有趣案例”的案件特别感兴趣
QObject::deleteLater()
这是结果:
class A : public QObject
{
public:
A() : QObject() {};
virtual ~A() { qDebug() << "Destructor of A"; }
};
class B : public A
{
public:
B() : A() {}
~B() { qDebug() << "Destructor of B"; }
};
int main(int argc, char*argv[])
{
qDebug() << "QT version " << QT_VERSION_STR;
qDebug() << "+++++++++++++++++++";
{
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<A>(new A(), &QObject::deleteLater)";
qDebug() << "Expected:";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<A>(new A(), &QObject::deleteLater);
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<A>(new A())";
qDebug() << "Expected:";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<A>(new A());
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "Test: QSharedPointer<B> sp = QSharedPointer<B>(new B(), &QObject::deleteLater)";
qDebug() << "Expected:";
qDebug() << "Destructor of B";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<B> sp = QSharedPointer<B>(new B(), &QObject::deleteLater);
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "Test: QSharedPointer<B> sp = QSharedPointer<B>(new B())";
qDebug() << "Expected:";
qDebug() << "Destructor of B";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<B> sp = QSharedPointer<B>(new B());
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "INTERESTING CASE";
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<B>(new B(), &QObject::deleteLater)";
qDebug() << "Expected:";
qDebug() << "Destructor of B";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<B>(new B(), &QObject::deleteLater);
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "INTERESTING CASE";
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<B>(new B())";
qDebug() << "Expected:";
qDebug() << "Destructor of B";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<B>(new B());
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<A>(new B(), &QObject::deleteLater)";
qDebug() << "Expected:";
qDebug() << "Destructor of B";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<A>(new B(), &QObject::deleteLater);
}
qDebug() << "-------------------";
qDebug() << "+++++++++++++++++++";
{
qDebug() << "IT SHOULD NOT WORK AS EXPECTED BEFORE QT 5.8, AS SPECIFIED IN QT DOCUMENTATION";
qDebug() << "Test: QSharedPointer<A> sp = QSharedPointer<A>(new B())";
qDebug() << "Expected:";
qDebug() << "Destructor of B (NOT expected before Qt 5.8)";
qDebug() << "Destructor of A";
qDebug() << "Result:";
QSharedPointer<A> sp = QSharedPointer<A>(new B());
}
qDebug() << "-------------------";
}
编辑:
我知道QObject::deleteLater的行为,尤其是:
“如果在主事件循环停止后调用deleteLater(),则不会删除该对象。从Qt 4.8开始,如果在没有运行事件循环的线程中的对象上调用deleteLater(),则线程结束后,对象将被破坏。”
但是,由于我使用的是Qt 5.7,因此无论如何我都希望在函数的末尾调用析构函数,这是我启动的唯一线程(其他线程最终由Qt启动)
编辑2
(我还编辑了标题)
(问题可能比我预期的要复杂。)
据我所知,没有主线程。所有线程都是相等的,第一个线程也应使用main函数隐式创建。无论如何都应该没什么特别的,对吗?
然后,为什么退出主线程不会以正确的方式删除QT version 5.7.1
+++++++++++++++++++
Test: QSharedPointer<A> sp = QSharedPointer<A>(new A(), &QObject::deleteLater)
Expected:
Destructor of A
Result:
-------------------
+++++++++++++++++++
Test: QSharedPointer<A> sp = QSharedPointer<A>(new A())
Expected:
Destructor of A
Result:
Destructor of A
-------------------
+++++++++++++++++++
Test: QSharedPointer<B> sp = QSharedPointer<B>(new B(), &QObject::deleteLater)
Expected:
Destructor of B
Destructor of A
Result:
-------------------
+++++++++++++++++++
Test: QSharedPointer<B> sp = QSharedPointer<B>(new B())
Expected:
Destructor of B
Destructor of A
Result:
Destructor of B
Destructor of A
-------------------
+++++++++++++++++++
INTERESTING CASE
Test: QSharedPointer<A> sp = QSharedPointer<B>(new B(), &QObject::deleteLater)
Expected:
Destructor of B
Destructor of A
Result:
-------------------
+++++++++++++++++++
INTERESTING CASE
Test: QSharedPointer<A> sp = QSharedPointer<B>(new B())
Expected:
Destructor of B
Destructor of A
Result:
Destructor of B
Destructor of A
-------------------
+++++++++++++++++++
Test: QSharedPointer<A> sp = QSharedPointer<A>(new B(), &QObject::deleteLater)
Expected:
Destructor of B
Destructor of A
Result:
-------------------
+++++++++++++++++++
IT SHOULD NOT WORK AS EXPECTED BEFORE QT 5.8, AS SPECIFIED IN QT DOCUMENTATION
Test: QSharedPointer<A> sp = QSharedPointer<A>(new B())
Expected:
Destructor of B (NOT expected before Qt 5.8)
Destructor of A
Result:
Destructor of B
Destructor of A
-------------------
呢?
我发布的是一个测试,在我的真实应用程序中我确实有一个QSharedPointer
循环,但是在循环停止后,析构函数被称为。
我希望然后在线程结束时(即我退出主循环时)调用exec()
s函数。
PS:如@dave所说,为了获得所有析构函数,我需要一个deleteLater()
循环,但这将是我的应用程序中的第二个循环,即:
exec()
在QTimer::singleShot(0, [](){qApp->exit();});
a.exec(); // a is my QApplication
之前。
我为什么需要它?有可能避免吗?
答案 0 :(得分:4)
使用deleteLater()代替,这将导致事件循环在将所有未决事件传送给对象后删除该对象。
由于没有QApplication
对象,因此程序中没有事件循环。因此,所有对deleteLater()
的调用都不会做任何事情。
如果通过实例化一个QApplication
并在其上调用exec
来引入一个事件循环,那么您会看到被误解的析构函数被调用:
在main()的末尾:
QApplication a(argc, argv);
a.exec();
其他输出:
Destructor of A
Destructor of B
Destructor of A
Destructor of B
Destructor of A
Destructor of B
Destructor of A
编辑
由于已经存在一个事件循环(如注释中所述),因此问题是QSharedPointer
对象在事件循环已经完成之后 被删除。 (又返回exec()
)
解决方案是将QCoreApplication::aboutToQuit
信号与将删除QSharedPointer
对象的函数或lambda连接。 (或者在这种情况下,清除包含这些内容的列表)
这样,事件循环就有机会在完成操作之前销毁所指向的对象。