简体   繁体   English

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

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

Good Morning everyone, 大家,早安,

I am using QSharedPointer with my classes derived from QObject . 我正在使用QSharedPointer和我从QObject派生的类。 Since they use the signal/slot mechanism, I must use the QObject::deleteLater() in order to properly destroy them, see for example: 由于它们使用信号/槽机制,我必须使用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." ~QObject() :“等待传递挂起事件时删除QObject会导致崩溃。如果QObject存在于与当前正在执行的线程不同的线程中,则不能直接删除它。使用deleteLater()代替,导致事件循环在所有挂起事件传递给它后删除对象。“

QSharedPointer and QObject::deleteLater QSharedPointer和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" QSharedPointer(X * ptr,Deleter d) :“删除器参数d指定此对象的自定义删除器。当强引用计数降为0时,将调用自定义删除器,而不是运算符delete()。这很有用,例如,用于在QObject上调用deleteLater()而不是“

Also pls notice that in the previous link it is written 另请注意,在上一个链接中,它是写的

"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模板参数T不相同,也会使用指向X类型的指针调用自定义删除函数。”,

but this is a quite different behaviour respect to the constructor QSharedPointer(X *ptr) that says: 但这与构造函数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.8开始,当对这个QSharedPointer的最后一个引用被破坏时,ptr将通过调用X的析构函数来删除(即使X与QSharedPointer的模板参数T不同)。之前,T的析构函数被调用。” - (I am using Qt 5.7, so I expect the ~T destructor) - (我使用Qt 5.7,所以我期待~T析构函数)

Well, at the end what I want to achieve is to call the proper destructor (of a child class) using the QSharedPointer , but since it is a QObject I need to use QObject::deleteLater() , but in my tests I am not able to achieve my goal. 好吧,最后我想要实现的是使用QSharedPointer调用正确的析构函数(子类),但由于它是一个QObject我需要使用QObject::deleteLater() ,但在我的测试中我不是能够实现我的目标。

I post a simple test and the result I had. 我发布了一个简单的测试和我的结果。

Can you pls tell me if I am doing something wrong? 你能告诉我,如果我做错了吗?

It is correct what I expect from the test? 我对测试的期望是正确的吗?

I am particularly interested in the case labeled with "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() << "-------------------";
}

And this is the result: 这就是结果:

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

EDITING: 编辑:

I am aware of QObject::deleteLater behaviour, in particular of: 我知道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." “如果在主事件循环停止后调用deleteLater(),则不会删除该对象。从Qt 4.8开始,如果在一个没有运行事件循环的线程中的对象上调用deleteLater(),则该对象将是线程结束时销毁。“

But since I am using Qt 5.7, I expect anyway the destructor to be called at the end of my function, that is the only thread that I start (a part the other threads started eventually by Qt) 但是因为我使用的是Qt 5.7,所以无论如何我都希望在我的函数结束时调用析构函数,这是我开始的唯一线程(其他线程最终由Qt启动的部分)

EDITING 2 编辑2

(I also edited the title) (我也编辑了标题)

(The question maybe is getting more complicated than I expected.) (问题可能比我预期的要复杂得多。)

As far as I know, there is no main thread. 据我所知,没有主线。 All threads are equal, also the first one that should be implicitly created with the main function. 所有线程都是相同的,也是应该使用main函数隐式创建的第一个线程。 It should be not special in any way, is that correct? 它应该不是特别的,这是正确的吗?

Then, why exiting the main thread will not delete the QSharedPointer s in the proper way? 那么,为什么退出主线程不会以正确的方式删除QSharedPointer

The one I posted is a test, in my real application I do have a exec() loop, but the destructors are called AFTER the loop is stopped. 我发布的是一个测试,在我的实际应用程序中我有一个exec()循环,但是在循环停止后调用析构函数。

I expect then the deleteLater() s function be called when the thread ends, that is when I exit my main loop. 我希望然后在线程结束时调用deleteLater()函数,即当我退出主循环时。

PS: in order to get all the destructors, I needed a exec() loop, as @dave said, but this would be the second loop in my application, that is: PS:为了获得所有的析构函数,我需要一个exec()循环,正如@dave所说,但这将是我的应用程序中的第二个循环,即:

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

at the end just before the return . return之前的最后。

Why I need it? 为什么我需要它? it is possible to avoid it? 有可能避免它吗?

Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it. 改为使用deleteLater(),这将导致事件循环在所有挂起事件传递给它之后删除该对象

You do not have an event loop in your program, since there is no QApplication object. 您的程序中没有事件循环,因为没有QApplication对象。 Thus, all calls to deleteLater() will not do anything. 因此,对deleteLater()所有调用都不会执行任何操作。

If you introduce an event loop by instanciating a QApplication and calling exec on it, you will see the mising destructors being called: 如果通过实例化QApplication并在其上调用exec来引入事件循环,您将看到正在调用的mising析构函数:

at the end of main(): 在main()的末尾:

QApplication a(argc, argv);
a.exec();

additional output: 额外输出:

Destructor of A
Destructor of B
Destructor of A
Destructor of B
Destructor of A
Destructor of B
Destructor of A

EDIT 编辑

Since there already was an event loop (as discussed in the comments), the problem was that the QSharedPointer objects got deleted after the event loop had already finished. 由于已经有一个事件循环(如评论中所讨论的),问题是在事件循环结束 QSharedPointer对象被删除了。 (Aka exec() returned) (Aka exec()返回)

The solution to that was to connect the QCoreApplication::aboutToQuit signal with a function or lambda that will delete the QSharedPointer objects. 解决方法是将QCoreApplication::aboutToQuit信号与将删除QSharedPointer对象的函数或lambda连接起来。 (Or in this case, clear a list containing those) (或者在这种情况下,清除包含这些的列表)

This way, the event loop has the chance to destruct the objects being pointed to before finishing. 这样,事件循环有机会在完成之前破坏被指向的对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM