Как правильно использовать деструкторы с QSharedPointer <QObject>ПОСЛЕ остановки цикла exec? - PullRequest
0 голосов
/ 11 февраля 2019

Доброе утро всем,

Я использую QSharedPointer с моими классами, полученными из QObject.Поскольку они используют механизм сигнала / слота, я должен использовать QObject::deleteLater(), чтобы правильно их уничтожить, см., Например:

~ QObject () : «Удаление объекта QObject во время ожидания»ожидающие доставки события могут вызвать сбой. Вы не должны удалять QObject напрямую, если он существует в потоке, отличном от того, который выполняется в данный момент. Вместо этого используйте deleteLater (), что приведет к тому, что цикл событий удалит объект после всех ожидающихсобытия были переданы ему. "

QSharedPointer и QObject :: deleteLater

QSharedPointer (X * ptr, Deleter d) :" TheПараметр deleteter d задает пользовательское средство удаления для этого объекта. Пользовательское средство удаления вместо оператора delete () вызывается, когда счетчик сильных ссылок падает до 0. Это полезно, например, для вызова deleteLater () для объекта QObject."

Также обратите внимание, что в предыдущей ссылке написано

" Обратите внимание, что пользовательская функция удаления будетвызываться с указателем на тип X, даже если параметр шаблона QSharedPointer не совпадает. ",

, но это совсем другое поведение по отношению к конструктору QSharedPointer (X * ptr) , который говорит:

"Начиная с Qt 5.8, когда последняя ссылка на этот QSharedPointer будет уничтожена, ptr будет удален путем вызова деструктора X (даже если 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)

EDITING 2

(я также отредактировал заголовок)

(Вопрос, возможно, становится более сложным, чем я ожидал.)

Насколько я знаю, основной темы нет. Все темыравно, также первое, которое должно быть неявно создано с помощью функции main.Это не должно быть особенным, верно?

Тогда, почему выход из основного потока не удалит QSharedPointer sправильно?

Тот, который я разместил, является тестом, в моем реальном приложении у меня есть цикл exec(), но деструкторы называются ПОСЛЕ того, как цикл остановлен.

Iожидайте тогда deleteLater() s фуВызов будет вызван, когда закончится поток, то есть когда я выйду из своего основного цикла.

PS: чтобы получить все деструкторы, мне нужен цикл exec(), как сказал @dave, но это будетвторой цикл в моем приложении, то есть:

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

в конце непосредственно перед return.

Зачем мне это нужно?можно этого избежать?

1 Ответ

0 голосов
/ 11 февраля 2019

Используйте взамен 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

EDIT

Поскольку уже был цикл обработки событий (как обсуждалось в комментариях), проблема заключалась в том, что объекты QSharedPointer были удалены после цикла обработки событий уже завершена.(Aka exec() вернулось)

Решением этой проблемы было соединить сигнал QCoreApplication::aboutToQuit с функцией или лямбда-выражением, которые будут удалять объекты QSharedPointer.(Или в этом случае очистите список, содержащий их)

Таким образом, цикл обработки событий может разрушить объекты, на которые указывает указатель до завершения.

...