4

大家,早安,

我正在使用QSharedPointer我的类派生自QObject. 由于它们使用信号/插槽机制,我必须使用QObject::deleteLater()才能正确销毁它们,例如:

~QObject():“在等待传递的未决事件时删除 QObject 可能会导致崩溃。如果 QObject 存在于与当前执行的线程不同的线程中,则不得直接删除它。请改用 deleteLater(),这将导致事件循环在所有未决事件都传递给它之后删除该对象。”

QSharedPointer 和 QObject::deleteLater

QSharedPointer(X *ptr, Deleter d) : "deleter 参数 d 指定此对象的自定义删除器。当强引用计数降至 0 时,将调用自定义删除器,而不是运算符 delete()。这很有用,例如,改为在 QObject 上调用 deleteLater()"

另请注意,在上一个链接中它是这样写的

"请注意,即使 QSharedPointer 模板参数 T 不一样,也会使用指向 X 类型的指针调用自定义删除器函数。",

但这与构造函数QSharedPointer(X *ptr)的行为完全不同,它说:

“从 Qt 5.8 开始,当对这个 QSharedPointer 的最后一个引用被销毁时,将通过调用 X 的析构函数来删除 ptr(即使 X 与 QSharedPointer 的模板参数 T 不同)。以前,调用了 T 的析构函数。” - (我使用的是 Qt 5.7,所以我期待~T析构函数)

好吧,最后我想要实现的是使用 调用正确的析构函数(子类的)QSharedPointer,但由于它是QObject我需要使用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() << "-------------------";
}

这是结果:

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
-------------------

编辑:

我知道QObject::deleteLater行为,特别是:

“如果在主事件循环停止后调用 deleteLater(),则不会删除对象。从 Qt 4.8 开始,如果在没有运行事件循环的线程中调用 deleteLater(),则该对象将被线程完成时销毁。”

但是由于我使用的是 Qt 5.7,所以无论如何我希望在函数结束时调用析构函数,这是我启动的唯一线程(其他线程最终由 Qt 启动的一部分)

编辑 2

(我还编辑了标题)

(问题可能比我预期的要复杂。)

据我所知,没有主线程。所有线程都是平等的,也是应该使用 main 函数隐式创建的第一个线程。它不应该以任何方式特别,对吗?

那么,为什么退出主线程不会QSharedPointer以正确的方式删除 s 呢?

我发布的是一个测试,在我的真实应用程序中我确实有一个exec()循环,但是在循环停止之后调用析构函数。

我希望deleteLater()在线程结束时调用 s 函数,即当我退出主循环时。

PS:为了获得所有的析构函数,我需要一个exec()循环,正如@dave 所说,但这将是我应用程序中的第二个循环,即:

QTimer::singleShot(0, [](){qApp->exit();});
a.exec(); // a is my QApplication

最后就在return.

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

4

1 回答 1

4

改用 deleteLater() ,这将导致事件循环在所有未决事件都传递给它之后删除对象。

您的程序中没有事件循环,因为没有QApplication对象。因此,所有调用deleteLater()都不会做任何事情。

QApplication如果您通过实例化 a并调用它来引入事件循环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 连接起来。(或者在这种情况下,清除包含这些的列表)

这样,事件循环就有机会在完成之前破坏指向的对象。

于 2019-02-11T10:54:31.977 回答