[解決済み】C++のコードを終了させる方法
質問
C++のコードで、ある条件を満たした場合に実行を停止させたいのですが、その方法がよくわかりません。つまり、どの時点でも、もし
if
ステートメントが真であれば、このようにコードを終了させます。
if (x==1)
{
kill code;
}
解決方法は?
いくつかの方法がありますが、まず、なぜオブジェクトのクリーンアップが重要なのかを理解する必要があり、そのために
std::exit
は、C++プログラマーの間では疎まれています。
RAIIとスタックアンワインド
C++では、以下のようなイディオムが使用されています。
RAII
これは簡単に言うと、オブジェクトはコンストラクタで初期化し、デストラクタで後始末を行うべきだという意味です。例えば
std::ofstream
コンストラクタでファイルを開き、ユーザが出力操作を行い、最後にライフサイクルの終わり(通常はスコープによって決定されます)にデストラクタが呼ばれ、本質的にファイルを閉じ、書き込まれた内容をディスクにフラッシュするのです。
もし、デストラクタでファイルをフラッシュして閉じられなかったら、どうなるのでしょうか? 知るか! でも、もしかしたら、ファイルに書き込むはずのデータが全部書き込まれないかもしれない。
例えば、次のようなコードを考えてみましょう。
#include <fstream>
#include <exception>
#include <memory>
void inner_mad()
{
throw std::exception();
}
void mad()
{
auto ptr = std::make_unique<int>();
inner_mad();
}
int main()
{
std::ofstream os("file.txt");
os << "Content!!!";
int possibility = /* either 1, 2, 3 or 4 */;
if(possibility == 1)
return 0;
else if(possibility == 2)
throw std::exception();
else if(possibility == 3)
mad();
else if(possibility == 4)
exit(0);
}
それぞれの可能性で起こることは
-
可能性1
Return は基本的に現在の関数スコープから離れるので、そのライフサイクルの終わりを知ることができる。
os
したがって、デストラクタを呼び出し、ファイルを閉じてディスクにフラッシュすることによって、適切なクリーンアップを行うことができます。 - 可能性2. 例外を投げると、現在のスコープにあるオブジェクトのライフサイクルも管理されるため、適切なクリーンアップが行われる...
-
可能性3.
スタック巻き戻しの実行です。で例外が発生しても
inner_mad
のスタックを調べます。mad
とmain
を含むすべてのオブジェクトが適切に破壊され、適切なクリーンアップが行われるようになります。ptr
とos
. -
可能性4.
さて、ここで?
exit
はCの関数で、C++のイディオムを意識していませんし、互換性もありません。それは はしません。 を含むオブジェクトのクリーンアップを実行します。os
を全く同じスコープで使用することができます。そのため、ファイルが適切に閉じられず、そのためにコンテンツが書き込まれない可能性があります。 -
その他の可能性
暗黙の了解で、メインスコープを離れるだけです。
return 0
そのため、可能性1と同じ効果、つまり適切なクリーンアップを行うことができます。
しかし、今話したこと(主に2番目と3番目の可能性)については、それほど確信を持ってはいけません。
考えられる方法 終了
メインから戻る!
可能な限りそうすべきです。常に、main から適切な終了ステータスを返してプログラムから戻ることを好むものです。
プログラムの呼び出し元や、場合によってはオペレーティングシステムは、あなたのプログラムが行うはずだったことがうまくいったかどうかを知りたいと思うかもしれません。これと同じ理由で、ゼロまたは
EXIT_SUCCESS
は、プログラムが正常に終了したことを示すシグナルで
EXIT_FAILURE
を使えばプログラムの終了が失敗したことを知らせることができます。その他の形式の戻り値は、実装で定義されています (
§18.5/8
).
しかし、コールスタックのかなり奥まで入っていて、全部を返すのは骨が折れるかもしれません...。
[例外を発生させない
例外をスローすると、スタックの巻き戻しにより、以前のスコープにあるすべてのオブジェクトのデストラクタが呼び出され、適切なオブジェクトのクリーンアップが行われます。
しかし、ここで
キャッチ
! 投げられた例外が処理されなかったときにスタックの巻き戻しを行うかどうかは実装で決まっている
(catch(...)節によって)
があっても、あるいは
noexcept
関数をコールスタックの真ん中に置くことです。これは、以下のように記載されています。
§15.5.1 [except.terminate]の項参照。
:
状況によっては、例外処理を放棄して、より巧妙なエラー処理技術を使用しなければならない場合があります。[注:このような状況とは
[...]
- 例外処理機構がスローされた例外のハンドラを見つけられない場合 (15.3) や、 ハンドラの検索 (15.3) で関数の一番外側のブロックが
noexcept
-指定 が例外を許さない場合(15.4)、または[...]です。[...]
このような場合、std::terminate()が呼び出されます(18.8.3)。マッチするハンドラが見つからない場合、 std::terminate() が呼ばれる前にスタックが巻き戻されるかどうかは実装で決まります。 [...]
だから、キャッチしなければならないのです
例外を投げて、メインでキャッチしてください。
捕捉されない例外はスタック巻き戻しを行わない場合があるため
(その結果、適切なクリーンアップが行われない)。
で例外をキャッチし、終了ステータスを返さなければなりません。
EXIT_SUCCESS
または
EXIT_FAILURE
).
ということで、おそらく良い設定としては
int main()
{
/* ... */
try
{
// Insert code that will return by throwing a exception.
}
catch(const std::exception&) // Consider using a custom exception type for intentional
{ // throws. A good idea might be a `return_exception`.
return EXIT_FAILURE;
}
/* ... */
}
[しないでください] std::exit
これはスタックの巻き戻しを一切行わず、スタック上のアライブオブジェクトもそれぞれのデストラクタを呼び出してクリーンアップを実行しません。
で強制されます。 §3.6.1/4 [基本.start.init]を参照してください。 :
現在のブロックを離れずにプログラムを終了する (例えば、関数 std::exit(int) (18.5) を呼び出す) ことは、自動保存期間を持つオブジェクトを破壊しません (12.4) . 静的ストレージやスレッドストレージの持続時間を持つオブジェクトの破壊中に std::exit が呼び出されてプログラムが終了した場合、そのプログラムは未定義の動作をします。
今考えてみてください、なぜそのようなことをするのでしょうか?あなたはどれだけのオブジェクトを痛めつけましたか?
他の[同じくらい悪い]代替案
プログラムを終了させる方法は他にもあります (クラッシュ以外の) しかし、それらは推奨されていません。ただ、わかりやすくするために、ここで紹介することにします。どのように 通常のプログラム終了 はありません。 はスタックの巻き戻しを意味しますが オーケー の状態です。
-
std::_Exit
は、通常のプログラム終了を引き起こし、それで終わりです。 -
std::quick_exit
を呼び出すと、通常のプログラム終了となりstd::at_quick_exit
ハンドラで、他のクリーンアップは行われません。 -
std::exit
は通常のプログラム終了を引き起こし、その後std::atexit
ハンドラです。その他、静的オブジェクトのデストラクタの呼び出しなど、様々なクリーンアップが行われます。 -
std::abort
は、プログラムの異常終了を引き起こし、クリーンアップは行われません。これは、プログラムが本当に、本当に予期せぬ方法で終了した場合に呼ばれるべきものである。これは何もしませんが、OSに異常終了のシグナルを送ります。システムによっては、この場合コアダンプを実行します。 -
std::terminate
が呼び出されます。std::terminate_handler
を呼び出します。std::abort
をデフォルトで使用します。
関連
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】致命的なエラー LNK1169: ゲームプログラミングで1つ以上の多重定義されたシンボルが発見された
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] なぜC++はPythonよりもstdinからの行の読み込みが遅いのですか?
-
[解決済み] Linux上で動作するC++コードのプロファイリングを行うにはどうすればよいですか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み】高放射能環境下で使用するアプリケーションのコンパイルについて
-
[解決済み】なぜC++プログラマは'new'の使用を最小限に抑えなければならないのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラー。アーキテクチャ x86_64 に対して未定義のシンボル
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み] 関数は return 文を1つだけ持つべきですか?