web-dev-qa-db-ja.com

どのような状況でC ++デストラクタが呼び出されないのですか?

デストラクタは、スタックの通常のアンワインド時と例外がスローされたときに呼び出されますが、exit()が呼び出されたときは呼び出されません。

私のデストラクタが呼び出されない他のケースはありますか? SIGINTやSIGSEGVなどの信号はどうですか? SIGSEGVの場合は呼び出されないと思いますが、SIGNINTの場合は呼び出されますが、どの信号がスタックを巻き戻すかをどのように知ることができますか?

彼らが呼ばれない他の状況はありますか?

44
WilliamKF

彼ら[デストラクタ]が呼び出されない他の状況はありますか?

  1. 長いジャンプ:これらは自然なスタックの巻き戻しプロセスを妨害し、C++で未定義の動作を引き起こすことがよくあります。
  2. 早期終了(すでに指摘しましたが、例外がスローされた結果としてすでにスタックが巻き戻されている間にスローすると、未定義の動作が発生するため、dtorからスローしてはいけません)
  3. コンストラクタからスローしても、クラスのdtorは呼び出されません。これが、ctor内の複数の異なるポインター(スマートポインターではない)によって管理される複数のメモリブロックを割り当てる場合、関数レベルのtryブロックを使用するか、初期化子リストの使用を避け、ctorにtry/catchブロックを含める必要がある理由です。 body(または、クラスdtorが呼び出されなくても、イニシャライザリストでこれまでに正常に初期化されたメンバーは破棄されるため、scoped_ptrのようなスマートポインタを使用するだけです)。
  4. 指摘したように、ベースポインタを介してクラスが削除されたときにdtorを仮想化できないと、サブクラスdtorの呼び出しに失敗する可能性があります(未定義の動作)。
  5. 演算子new/new []呼び出しに対して一致する演算子delete/delete []を呼び出さない(未定義の動作-dtorの呼び出しに失敗する可能性があります)。
  6. 割り当て解除セクションでカスタムメモリアロケータを使用して新しい配置を使用するときに、dtorを手動で呼び出せない。
  7. Copy ctorsを呼び出さずに、あるメモリブロックを別のメモリブロックにコピーするだけのmemcpyのような関数を使用します。 mem *関数は、クラスのプライベートデータをブルドーズしたり、vtableを上書きしたりするため、C++では致命的です。結果は通常、未定義の動作になります。
  8. 不完全な型でのいくつかのスマートポインタ(auto_ptr)のインスタンス化。これを参照してください ディスカッション
49
stinky472

C++標準では、特定の信号の処理方法については何も述べられていません。多くの実装ではSIGINTなどがサポートされていない可能性があります。exit()またはabort()またはterminate()が呼び出されます。

編集: C++標準をすばやく検索したところ、信号がオブジェクトの存続期間とどのように相互作用するかを指定するものが見つかりません-おそらく私よりも優れた標準を持つ人-fuは何かを見つけることができますか?

さらに編集:別の質問に答えているときに、私はこれを標準で見つけました:

スコープを終了すると(ただし、達成されます)、デストラクタ(12.4)は、そのスコープで宣言された自動ストレージ期間(3.7.2)のすべての構築済みオブジェクト(名前付きオブジェクトまたは一時オブジェクト)に対して、宣言の逆の順序で呼び出されます。

したがって、信号を受信したときにデストラクタを呼び出す必要があるようです。

7
anon

シグナルそれ自体現在のスレッドの実行に影響を与えません。したがって、デストラクタの呼び出しは影響を受けません。これは、オブジェクトが存在する独自のスタックを持つ異なる実行コンテキストであるためです。存在しない。これは割り込みのようなものです。実行コンテキストの外部で処理され、処理されると、制御がプログラムに返されます。

マルチスレッドの場合と同じように、C++ 言語はシグナルの概念を知りません。これら2つは互いに完全に直交しており、2つの無関係な標準によって指定されています。それらがどのように相互作用するかは、どちらの標準にも違反しない限り、実装次第です。

補足として、別のケースは、オブジェクトのデストラクタが呼び出されない場合、そのコンストラクタが例外をスローする場合です。ただし、メンバーのデストラクタは引き続き呼び出されます。

3
Alex B

それらが呼び出されないもう1つのケースは、ポリモーフィズムを使用していて、ベースデストラクタを仮想化していない場合です。

3
DanDan

関数またはメソッドにthrows仕様があり、仕様でカバーされていないものをスローする場合、デフォルトの動作はすぐに終了します。スタックは巻き戻されず、デストラクタは呼び出されません。

POSIXシグナルは、オペレーティングシステム固有の構造であり、C++オブジェクトスコープの概念はありません。一般に、シグナルをトラップし、グローバルフラグ変数を設定し、シグナルハンドラーの終了後に、C++コードで後で処理する以外は、シグナルに対して何もできません。

GCCの最近のバージョンでは、同期シグナルハンドラー内から例外をスローできます。これにより、予期される巻き戻しと破棄のプロセスが発生します。ただし、これはオペレーティングシステムとコンパイラに固有のものです。

2
karunski

abort Standardが言うように、自動または静的ストレージ期間のオブジェクトのデストラクタを実行せずにプログラムを終了します。その他の状況については、実装固有のドキュメントを読む必要があります。

ここにはたくさんの答えがありますが、まだ不完全です!

デストラクタが実行されない別のケースを見つけました。これは、例外がライブラリの境界を越えてキャッチされたときに常に発生します。

詳細はこちらをご覧ください:

例外がスローされたときにデストラクタは実行されません(スタックの巻き戻しなし)

2
Elmue

デストラクタが呼び出される状況は基本的に2つあります。関数の最後(または例外)でスタックアンワインド時に、誰か(または参照カウンタ)がdeleteを呼び出した場合。

1つの特別な状況は、静的オブジェクトに見られることです。これらは、プログラムの最後にat_exitを介して破棄されますが、これは2番目の状況です。

どのシグナルがat_exitを通過するかによって異なり、kill -9はプロセスをすぐに強制終了し、他のシグナルは終了するように指示しますが、シグナルコールバックにどの程度正確に依存します。

1
nob