おはようございます。
QSharedPointer
から派生したクラスで使用していますQObject
。これらはシグナル/スロット メカニズムを使用するQObject::deleteLater()
ため、適切に破棄するには を使用する必要があります。例を参照してください。
~QObject() : "保留中のイベントが配信されるのを待っているときに 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 への最後の参照が破棄されると、ptr は X のデストラクタを呼び出すことによって削除されます (X が QSharedPointer のテンプレート パラメータ T と同じでなくても)。以前は、T のデストラクタが呼び出されていました。」- (私は Qt 5.7 を使用しているので、~T
デストラクタを期待しています)
さて、最後に達成したいのは、を使用して(子クラスの)適切なデストラクタを呼び出すQSharedPointer
ことですが、それはQObject
を使用する必要があるためQObject::deleteLater()
、テストでは目標を達成できません。
簡単なテストと結果を投稿します。
私が何か間違ったことをしている場合は教えていただけますか?
テストに期待することは正しいですか?
私が特に興味を持っているのは、「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の動作、特に次のことを認識しています。
「メイン イベント ループが停止した後に deleteLater() が呼び出された場合、オブジェクトは削除されません。Qt 4.8 以降、イベント ループが実行されていないスレッドに存在するオブジェクトで deleteLater() が呼び出された場合、オブジェクトは削除されます。スレッドが終了すると破棄されます。」
しかし、私はQt 5.7を使用しているので、関数の最後にデストラクタが呼び出されることを期待しています.
編集 2
(タイトルも編集しました)
(質問は、予想以上に複雑になっているかもしれません。)
私の知る限り、メインスレッドはありません。すべてのスレッドは同等であり、メイン関数で暗黙的に作成される最初のスレッドでもあります。決して特別なものではないはずですよね?
では、なぜメイン スレッドを終了してもQSharedPointer
s が適切な方法で削除されないのでしょうか。
私が投稿したのはテストです。実際のアプリケーションではexec()
ループがありますが、ループが停止した後にデストラクタが呼び出されます。
deleteLater()
スレッドが終了したとき、つまりメインループを終了したときに s 関数が呼び出されることを期待しています。
PS: exec()
@dave が言ったように、すべてのデストラクタを取得するためにループが必要でしたが、これはアプリケーションの 2 番目のループになります。
QTimer::singleShot(0, [](){qApp->exit();});
a.exec(); // a is my QApplication
最後に の直前にreturn
。
なぜ必要なのですか?それを避けることは可能ですか?