exec 循环停止后如何正确使用 QSharedPointer<QObject> 的析构函数?

How to properly use destructors with QSharedPointer<QObject> AFTER the exec loop is stopped?

大家早上好,

我正在使用 QSharedPointer 和从 QObject 派生的 classes。由于他们使用 signal/slot 机制,我必须使用 QObject::deleteLater() 才能正确销毁它们,例如:

~QObject() : "Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it."

QSharedPointer and QObject::deleteLater

QSharedPointer(X *ptr, Deleter d): "The deleter parameter d specifies the custom deleter for this object. The custom deleter is called, instead of the operator delete(), when the strong reference count drops to 0. This is useful, for instance, for calling deleteLater() on a QObject instead"

另请注意previous link中写着

"Note that the custom deleter function will be called with a pointer to type X, even if the QSharedPointer template parameter T is not the same.",

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

"Since Qt 5.8, when the last reference to this QSharedPointer gets destroyed, ptr will be deleted by calling X's destructor (even if X is not the same as QSharedPointer's template parameter T). Previously, the destructor for T was called." -(我使用的是 Qt 5.7,所以我期望 ~T 析构函数)

好吧,最后我想要实现的是使用 QSharedPointer 调用正确的析构函数(child class),但是因为它是 QObject 我需要使用 QObject::deleteLater(),但在我的测试中我无法实现我的目标。

我post一个简单的测试和我得到的结果。

如果我做错了什么,你能告诉我吗?

我对测试的期望是正确的吗?

我对标有"INTERESTING CASE"

的案例特别感兴趣
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 行为,尤其是:

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

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

编辑 2

(我也编辑了标题)

(问题可能比我想象的要复杂。)

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

那么,为什么退出主线程并不能正常删除QSharedPointer呢?

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

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

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

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

最后 return

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

Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.

您的程序中没有事件循环,因为没有 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 连接起来。 (或者在这种情况下,清除包含这些的列表)

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