私が次のスニペットを持っていると仮定すると、それが管理する他のQTオブジェクトのqtoのデストラクタでdeleteLaterを呼び出しても安全ですか?
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyQTObject qto;
qto.show();
return a.exec();
}
私はリークディテクターを使用してこのような同様のコードを分析し、deleteLaterが呼び出されたすべてのオブジェクトは、通常の削除で呼び出しを置き換えない限り、正しく割り当て解除されませんでした。これを正しく理解していれば、deleteLaterは削除イベントをQTメッセージキューに登録するだけです。これは、QTOのデストラクタがメインのスコープの最後で呼び出され、QTメッセージループがa.execからの戻りですでに終了しているという問題でしょうか?したがって、削除イベントは処理されないため、実際には何もないため、メッセージキューにプッシュされることすらありません。
私が理解しているように、スロットへの呼び出し内からオブジェクトを削除する必要がある場合、deleteLaterが最もよく使用されます。この場合に削除が使用され、スロットから戻るときにオブジェクトが参照されると、初期化されていないメモリへの参照が発生します。
したがって、deleteLaterは、スロットから戻ったときに、ある時点で処理されるイベントループにメッセージを配置することにより、そのオブジェクトを削除するように要求し、削除しても安全です。
デストラクタでdeleteLaterを使用すると、オブジェクトがスコープ外になり、そのマネージオブジェクトでdeleteLaterを呼び出す可能性が高くなりますが、イベントループがオブジェクトを削除する前に終了し、QApplication :: exec( )はイベントループを終了します。
この投稿はかなり古くなっていますが、自分で質問したときに出会いたかった答えを追加したいと思います。
deleteLater()
は、非同期操作と組み合わせると非常に便利です。特に、信号をラムダ関数に接続する最近の可能性があると私は思います。
非同期に実行したいlongComputation()
があるとします(マルチスレッドの意味ではなく、イベントループで実行をスケジュールする意味で)。あなたはこのようにすることができます:
_void MyClass::deferLongComputation()
{
QTimer* timer = new QTimer();
connect(timer,
&QTimer::timeout,
[this, timer](){this->longComputiation(); timer->deleteLater();});
timer->setSingleShot(true);
timer->start();
}
_
ここで、deleteLater()
は、義務が実行されたらQTimer
を安全に破棄し、そうでなければメモリリークを回避します。
QFutureWatcher
を使用したマルチスレッドでも同じパターンを使用できます。
質問は古いですが、私はこれを次の世代に残します)回答としてマークされた応答は正しいですが、奇妙に定式化されています。実際、あなたの質問には正しい答えが含まれています:
メッセージループはすでにa.execからの戻りで終了していますか?したがって、削除イベントは処理されないため、実際には何もないため、メッセージキューにプッシュされることもありません。
これはまさに起こっていることです。 deleteLater()
が行うすべてのことは、削除イベントを外部イベントループにポストするだけです。イベントが処理されると、オブジェクトが削除されます。ただし、外部イベントループがなく、実行フローの後半でイベントループが発生しない場合は、イベントがポストされないため、オブジェクトが削除されることはありません。
オブジェクトのデストラクタでdeleteLater()
を呼び出し、オブジェクトをスタックに置くと、オブジェクトがスコープ外になるとdeleteLater()
が呼び出されます。あなたの例では、「範囲外」はmain()
関数の右中括弧に遭遇したときに起こります。ただし、そのときまでに、a.exec()
(Qt Appのメインイベントループを表す)はすでに->イベントを返していません。これ以上ループします->deleteLater()
が呼び出されましたが、削除イベントをポストする場所がありません->オブジェクトは「deletedLater」であるはずでしたが、削除されません...
「deleteLater()を使用する場合」の部分について:
Kuba Ober回答:
一般的に、deleteLaterを使用する必要がある状況は限られています。おそらくあなたは単にそれを使うべきではありません...
それを聞かないでください、それは全体の答えとして絶対に間違っています。 この記事 を読んだ後、何をすべきか、何をすべきではないかを判断します。それは主にQtスレッドについてですが、記事では非同期プログラミングについても説明しています(そしてEmerald Weaponが述べたように、これはまさにdeleteLater()が何であったかですのために作成された)。
また、スマートポインターとQObjectの親の所有権は、deleteLater()
を使用した削除のスケジューリングとは関係ありません。これらの両方のテクニックは、実際には内部で単純なdelete
操作を使用しています。そして、記事が示すように、Emerald Weaponの答えが示すように:delete
は問題を解決しませんdeleteLater()
します。したがって、オブジェクトを削除する必要がある場合はdelete
を使用し、削除をスケジュールする必要がある場合はdeleteLater()
を使用します。
ところで、deleteLater()
でスマートポインターを使用したい場合は、deleterを指定できます。
_// Shared Pointer
QSharedPointer<MyObject> obj =
QSharedPointer<MyObject>(new MyObject, &QObject::deleteLater);
// Scoped Pointer
QScopedPointer<MyObject, QScopedPointerDeleteLater> customPointer(new MyObject);
_
そして最後に、それはデストラクタでdeleteLater()
を使用するエラー[〜#〜](〜#〜]ではありません子オブジェクト以外の場合はQObject
の。
deleteLater()
コマンドがイベントループによってのみ実行されることは正しいです。
Qtドキュメント for QObject
から:
このオブジェクトの削除をスケジュールします。
コントロールがイベントループに戻ると、オブジェクトは削除されます。この関数が呼び出されたときにイベントループが実行されていない場合(たとえば、QCoreApplication :: exec()の前にオブジェクトでdeleteLater()が呼び出された場合)、イベントループが開始されると、オブジェクトは削除されます。メインイベントループが停止した後にdeleteLater()が呼び出された場合、オブジェクトは削除されません。 Qt 4.8以降、実行中のイベントループのないスレッドに存在するオブジェクトでdeleteLater()を呼び出すと、スレッドが終了するとオブジェクトが破棄されます。
新しいイベントループの開始と終了(たとえば、モーダルダイアログを開くことによる)は、遅延削除を実行しないことに注意してください。オブジェクトを削除するには、コントロールはdeleteLater()が呼び出されたイベントループに戻る必要があります。
注:この関数を複数回呼び出しても安全です。最初の遅延削除イベントが配信されると、オブジェクトの保留中のイベントがイベントキューから削除されます。
QObjects
を削除するときにすべての子qto
を削除する場合は、qto
を親として作成してください。
一般的に、deleteLaterを使用する必要がある状況は限られています。ほとんどの場合、それを使用すべきではありません。
子以外のオブジェクトに対してQObjectのデストラクタで使用するとエラーになります。あなたが発見したように、QObjectsはイベントループが存在しないと破壊される可能性があります。たとえば、deleteLater
Qtモジュールなどのオブジェクトデストラクタにはqtbase
呼び出しはありません。
ここで注意する必要があります。たとえば、~QTcpServer()
はclose()
を呼び出してd->socketEngine->deleteLater()
を呼び出しますが、ソケットエンジンはすでにサーバーの子であり、 ~QObject()
とにかく。
私が知っている限りでは、MyQTObject
は次のいずれかを実行しているはずです。
QScopedPointer
やstd::unique_ptr
などのスマートポインタを使用して、