在执行循环停止后,如何与QSharedPointer <qobject>一起正确使用析构函数?

时间:2019-02-11 10:37:23

标签: c++ qt destructor qobject qsharedpointer

大家早上好

我将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 之前。

我为什么需要它?有可能避免吗?

1 个答案:

答案 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连接。 (或者在这种情况下,清除包含这些内容的列表)

这样,事件循环就有机会在完成操作之前销毁所指向的对象。