質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

【Qt】クラスのデストラクタが走った後に、スロットが動き続けてしまう

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,097

tuyudaku

score 61

発生しているものとしては
Segmentation faultになるのですが

色々デバッグしている中で何とか分かったのは
デストラクタ後にデストラクタが走ったクラスのスロットが動作し続けてしまっています
恐らくデストラクタが走る前に動いていたスロットだと思うのですが
そのせいで破棄されたクラスのオブジェクトにアクセスしようとしてセグ落ちが発生しているのだと思います
ふわふわした情報で申し訳ありません...

クラスが破棄されると同時に、動作中のスロットも停止させることはできないのでしょうか?

よろしくお願いします

追記

情報が少なく言葉足らずで申し訳ありません...
分かった情報などをまとめさせた頂きます

  • 該当スロットはデストラクタが走る前に、呼び出されたもの
  • 該当スロットではループの処理をしている、ループ中にデストラクタが走っています
  • ループ処理内ではQEventLoopQTimer::singleShotを使ったスリープを使って1秒間隔で処理するようにしてある(個人的にはコレが何か問題を起こしているのかと思っております)
  • 該当スロットのループ内とデストラクタにログを仕込みましたが、デストラクタのログ後にループのログが確認できているので、デストラクタ後にも動き続けていると判断しました
  • 回答に頂いたので、thisのアドレスをログで出力させたのですが、デストラクタ後に該当スロットループ内のログでセグ落ちが発生しました、破棄されたthisへのアクセスのせいだと思われるので、私が考えている問題は起きていると思われます
  • ループ内でローカル変数へのアクセスは問題が起きませんが、メンバ変数へのアクセスをするとセグ落ちが発生します

ここまで書いて、あることに気付いたのですが
そもそもセグ落ちしている関数はスロット内で呼ばれているprivateメンバ関数です...
実質スロットの処理ではあるとは思いますが一応情報だけでも...
また何か状況が変わらないかと思いemitで該当スロットとコネクトしてあるシグナルを呼び出していたところを、QTimer::singleShotで動作させてみたのですが状況は変わりませんでした

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

既に回答の通り、通常のQtの使い方をしている分には、オブジェクトの破棄後にそのオブジェクトのスロットが呼ばれる、ということはありえません。
もし発生しているとすれば、通常の使い方をしていないということです。例えば以下のような点に問題はありませんか?

  • 他のスレッドのオブジェクトが、該当のオブジェクトのポインタや参照を持っている(所有権管理違反)
  • 他のスレッドから AutoConnection を使わず DirectConnection でそのスロットにつないでいる
  • スロットをメンバー関数として直接呼んでいる
  • スレッドの止め方がマニュアル通りでない

また、運が良ければ、静的解析ツールや valgrind などのメモリ解析ツールを使うと手がかりが得られる場合もあります。


追記へ

要するにこういうことでしょうか?

SomeObject::slotA()
{
    ……前の処理……
    ループ {
        QEventLoop loop;
        connect(1秒の単発タイマー → loop の quit);
        loop.exec();

        ……次の処理…… ← ここで発生
    }
}

「QEventLoop を使ってスリープする」というのは間違った使い方です。
QEventLoop を呼ぶと、スロットが実行中にも関わらず、同じスレッドに属するあらゆるオブジェクトのあらゆるスロットを呼ぶことが可能になってしまいます。この状況で SomeObject が delete されないように厳密に管理することは極めて困難です。「気をつけて設計すればいい」という問題ではありません。

基本的に QEventLoop をスロットの中で呼ぶのはNGだとお考え下さい。
もしチームで開発しているのでしたら、コーディング規約で禁止することをお勧めします。

正しくは、例えば次のようにします。

SomeObject::slotA()
{
    ……前の処理……
    connect(1秒の単発タイマー → slotB);
    return;
}

SomeObject::slotB()
{
    ……次の処理……
    if (必要であれば) {
        connect(1秒の単発タイマー → slotB);
    }
}

このようにすればタイマーで待っている最中に this が delete されても問題ありません(少なくともセグフォが発生しないという点については)
なお、Qt 5 以降なので、スロットの代わりにラムダを使うこともできます。「ラムダとは何か?」から説明するのは大変なので、興味がお有りであればご自分でお調べ下さい。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/06 09:31

    >オブジェクトの破棄後にそのオブジェクトのスロットが呼ばれる
    言葉足らずで申し訳ありません...
    破棄後に呼ばれるのではなく、スロット処理中に破棄が起きてもスロットが動き続けている状態です

    キャンセル

  • 2019/09/09 18:21

    追記に反応がないですが、何かわからないところがありましたか?

    キャンセル

  • 2019/09/10 19:17

    >追記に反応がないですが、何かわからないところがありましたか?
    すみません、普通にサイトを見ていませんでした

    >「QEventLoop を使ってスリープする」というのは間違った使い方です。
    Qt sleepと調べると沢山出てくるため、これが一般的だと思っていました...
    情報ありがとうございます

    >もしチームで開発しているのでしたら、コーディング規約で禁止することをお勧めします。
    その様にしたいと思います

    >正しくは、例えば次のようにします。
    修正したソースは頂いた例のようになっています!
    何とかたどり着いた答えが合っているみたいでよかったです!
    ラムダに関してはそれなりに利用して活用しています!

    回答ありがとうございました!

    キャンセル

+1

質問の書き方がぼんやりしているため回答しづらいですが…。

Qt では、インスタンスのデストラクタが呼ばれた際に、そのオブジェクトのインスタンスへ接続されているシグナル・スロットは自動的に破棄されます。このため、通常であればそのような状況は起こらないはずです。

QObject::~QObject()

All signals to and from the object are automatically disconnected, and any pending posted events for the object are removed from the event queue. However, it is often safer to use deleteLater() rather than deleting a QObject subclass directly.

一度プロジェクトをクリーンビルドしてみて、それでも再現するようでしたら、どこかでメモリを破壊していたりしないかの確認をしてみてはいかがでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/06 09:29

    >一度プロジェクトをクリーンビルドしてみて
    それでも状況は変わりませんでした...

    キャンセル

+1

こんにちは。

QObject のインスタンスを delete する時にわざわざ disconnect する必要は無いそうです。
私もこの記述を信じて開発していますが、キューに並んでいるイベントがslot側のオブジェクト開放後に処理された経験はないです。

当該オブジェクトをdeleteする直前にそのアドレスをログに残し、当該スロットでも同様にthisのアドレスをログに残すことで、おっしゃる現象が発生しているのかどうか確認できると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/06 09:30

    >当該オブジェクトをdeleteする直前にそのアドレスをログに残し、当該スロットでも同様にthisのアドレスをログに残すことで、おっしゃる現象が発生しているのかどうか確認できると思います
    デストラクタ後にスロット内でのthisアドレスをログしようとしたところでセグ落ちが発生しました
    thisが破棄されているせいだとは思います

    キャンセル

  • 2019/09/06 14:17

    追記をみました。それはQtのシグナルースロット機構と無関係です。シグナルが発生したらスロットを呼び出すための機構に過ぎません。呼び出されたスロットがどのように動作するのかQtのシグナルースロット機構は何も関知しません。
    スロットは一種のメンバ関数に過ぎません。そのメンバ関数実行中に、そのメンバが属するオブジェクトをdeleteしてはいけないのは当たり前ですね。これはプログラマの責任です。

    キャンセル

  • 2019/09/06 16:35

    >そのメンバが属するオブジェクトをdeleteしてはいけないのは当たり前ですね。これはプログラマの責任です。
    なるほど、なんでそんな実装をしているんだ!ということですね...

    途中で処理を中断してでもオブジェクトを破棄したい場合はどうすればいいでしょうか...

    キャンセル

  • 2019/09/06 16:56

    う~~ん、当たり前ですが、その処理を中断させてからdeleteすれば良いです。
    このような中断処理はデッドロックしやすいので頭痛いのですが、慎重に設計するしかありません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る