385 :
デフォルトの名無しさん:2006/12/19(火) 06:47:46
C++でアプリを正常終了する時に例外を投げるのって邪道?
ずっと稼動してる様なアプリだと、シグナルがきたら終わるとかよくやるけど
そこで例外投げるってのはどうかと思う。
>例外は、それを取り巻くコードの
>構造と意味を、根底から覆します。例外は、プログラムの実行時
>セマンティックを一時的に繋ぎ変え、通常実行しているコードを迂回し、
>こういう状況でなければ決して実行されないコードを動作させます。
間違いってことはないが、明らかに設計が普通ではない
例外って飛んできたらexitするしかないような処理でしょ。
暗号化したコードを復元するためのシグナルに、例外を使ったことがある。
反省はしない。
どこから飛んできたのか探すのが大変だから、正常系の処理に例外は使うなってこった。遅いし。
明らかに復帰不可能な例外なら投げてもいいけど。
>>389 それが決まってるなら exit() すればいいわけで、例外投げる意味が無い。
>>392 exit()するとオブジェクトの始末がされないのでは?
exitするしかないような事態で、正常に後始末できるの?
>>392 ローカルオブジェクトのデストラクタは呼び出されないね。
意味が無いわけじゃないな。
だからといって例外の使い道がそんな場合に限定されるわけじゃないよ。
catch した人が処理を選択できるのが例外の重要な意義なんだから。
>>394 なんでできないと思うの?
exitするしかないような事態って、ディスクとかネットワークトラブルとか思い浮かべてたんだけど
そんな状態で正常に後始末できるのかなと
コード上のリソース(ヒープとかファイルハンドルとか)の後始末じゃね?
C++ だと例外安全かどうか気をつかうことになりそうだけどサ
C++に正解などない。
書きたいように書けばいいじゃないか。
しかし定石はあると言えないか?
Hoge::Hoge():pint( new int ){};
これは間違いだと言いたい。
Hoge::Hoge():pint( new int ){};
には別に悪いことはないんじゃない。
ただ、コンストラクタ/デストラクタで例外出すのはやめて欲しいがな
>>402 コンストラクタは別にいいだろ。他にどうやって失敗を通知するんだ?
コンストラト処理中に例外発生した半端なオブジェクトに対して デストラクタは呼ばれない
該当半端オブジェクトの、例外発生前までのうまくいっている部位の後始末をどうするかが面倒
ってことで、問題を簡単化するのに
>>402 な希望だと思う
それぞれのメンバが適切に RAII のルールを守っていれば話はもっと単純にできる。
それをせずに「コンストラクタから例外禁止」なんて新しいルールを作るのは良くない。
>例外発生前までのうまくいっている部位の後始末をどうするかが面倒
後始末が必要なものはクラスで包めばいいじゃん。
結局RAIIに徹しろっていう普通の関数の例外安全性の議論と大差ないよね>コンストラクタ
RAIIが面倒な場合は初期化子リストにtry-catch仕掛けることもできるし
一般にコンストラクタは強い例外安全性を達成するべきという主張なら十分納得できるんだけれど
class A {
Hoge *hoge; //どっちともコンストラクタで例外を出す可能性があるクラス
Hage *hage;
A()
try:hoge( new Hoge ), hage( new Hage ) { }
catch( ... )
{
初期化子にtry-catch掛けられるったって、こういう状況は非常に面倒くさい。
new Hageで例外でたらどーすんだ?
}
> 後始末が必要なものはクラスで包めばいいじゃん。
> 後始末が必要なものはクラスで包めばいいじゃん。
初期化子を使わずにコントロールしろということなのかな?
>>408 の例だと
A()
{
try { hoge = new Hoge; } catch(...) { throw ; }
try { hage = new Hage; } catch(...) { delete hoge; throw ; }
};
スマートポインタ使えばいいのに
class A
{
std::auto_ptr<Hoge> hoge;
std::auto_ptr<Hage> hage;
A() : hoge(new Hoge), hage(new Hage) {}
};
これでどちらで例外が投げられても大丈夫。
>>412 AのA のデストラクタが呼ばれないのは分かるけど、A::hoge と A::hage のデストラクタは呼ばれるの?
スマートポインタって自身のデストラクタ呼ばれないと機能しないような気がするのだが
>>413 コンストラクタが完了したものはデストラクタが呼ばれる。
new Hageで例外が出ても、hogeは構築済みだからデストラクタが呼ばれる。
415 :
413:2006/12/22(金) 19:57:38
>>414 てんきゅう。 自分もコード動かして確認した…
例外周りのメンバの取り扱いはややこしいねぇ
実際投げてる例外はおいといてくれw
class Hoge {
public:
Hoge() { std::cout << "Construct Hoge" << std::endl; }
~Hoge() { std::cout << "Destruct Hoge" << std::endl; }
};
class Hage {
public:
Hage() { int e=1; throw e; }
};
class A {
std::auto_ptr<Hoge> hoge;
std::auto_ptr<Hage> hage;
public:
A() : hoge(new Hoge), hage(new Hage) {}
};
int main() { try { A a; } catch (...) { } return 0; }
構築に成功したオブジェクトは必ず解体に成功する
構築に失敗したオブジェクトは解体する必要がない
何か気をつける余地が0になるのがRAIIのありがたいところ
new Hageで確保されたメモリは開放されんの?>auto_ptr
>>418 んーごめん。まったくわからん(´д`;
>>419 new Hage の動作を分解すると、
void* storage = operator new (sizeof(Hage));
try { new (storage) Hage; }
catch (...) { operator delete (storage); throw; }
こんな感じになってるわけよ。
ぉぉ。そうなんですか。
あざーす
だれもいない
じゃあ話題振るか。
システム内部は全てワイド文字で処理してるプログラムの例外って
みんなどうしてる?
std::exceptionのwhat()ってconst char*しか扱えないじゃない?
そうするとシステム内部のワイド文字列を例外に載せようとした時、
2つの解決方法があると思うんだ。
1つはその文字列をマルチバイト文字列にコンバートして
std::exception(の派生クラス)で投げる。
もう1つはワイド文字列を運べるwexceptionクラスを自作する。
1だと投げる時WCS>MBS、捕まえて表示するときMBS>WCSと
無駄な処理が発生する場合がある。
2だとSTLとかが投げる例外と自分のシステムが投げる例外が
同じ処理をする場合でも、いちいちcatch節を2つ書かなきゃならない。
まあ例外は全てMBSで扱って表示もMBSでしかしないってのもアリだけど
typedef struct what {
int id,
wchar *hoge;
} what;
exception((char *)w);
what()はデバッグ時にトレース出力くらいしか使ってないや。
>>423 一番堅いけれど面倒なやり方として,
エラーの識別子のみを std::exception に載せる std::string の情報とする扱い方.
エラーを人間に読めるようなメッセージに変換する必要があるならば,
メッセージカタログなりを使ってエラーの識別子とエラーメッセージとの対応を取る.
この場合,表示するメッセージを MBS とするか WCS とするかのポリシーは
メッセージカタログの責任範疇となって, std::exception と独立となるのが利点.
>>426 std::exceptionにstd::stringに埋めちゃうのは、std::stringのコピーコンストラクタ
が例外を発射する可能性があるから、やばいのでは。
例外処理の運用については、
>>423的な悩みも含めて Boost の
http://www.boost.org/more/error_handling.html がとてもよくまとまっていると思う。
(翻訳版はバージョンが古いのか、内容が違う)
あとはexception safty(例外安全性(?))やexception neutral (例外中立性(?))
なクラスの設計、実装について、Exceptional C++, More Exceptional C++,
Exceptional C++ Style の該当項目に目を通せばOK。
428 :
426:2007/01/15(月) 22:01:37
429 :
423:2007/01/15(月) 22:06:59
>>426 Win32APIのGetLastError()とFormatMessage()みたいなことかな。
できれば問題が起きた原因たるオブジェクト(の内容)を
例外ハンドリングしてるところまで持ってって表示してあげたいんだけど、
その方法じゃ無理だね。困った。
>>427 つまり↑みたいな野望は無駄な足掻きってことかな…。
staticなエラーメッセージ保存領域作っておいて
そこにコピーしてから例外飛ばすとか?
てか、もしかしたら例外で処理すべき問題じゃないのかもしれない…
>>423 規格にある std::exception::what() についての記述には、
wstring に変換して表示するのに適した NTMBS でもよい、とある。
規格に沿うようにしたいのなら、このアプローチがいいってことね。
>>429 >Win32APIのGetLastError()とFormatMessage()みたいなことかな。
みたいなことですね。
>できれば問題が起きた原因たるオブジェクト(の内容)を
>例外ハンドリングしてるところまで持ってって表示してあげたいんだけど、
エラーの文脈情報を付加したいという話ですよね。そのエラーに対応する
std::exceptionの派生クラスのメンバ変数に持たせれば可能なのでは。
>
>>427 >つまり↑みたいな野望は無駄な足掻きってことかな…。
いや、427ではstd::exceptionにstd::stringを「埋める」とやばいという話を
しましたけど、ポインタで(例えばstd::auto_ptr<std::string>)で持たせれば
問題はありません。string以外の文脈情報についても同様です。
今度はnewが例外を投げるのでは、という心配が。
>>432 例外クラスの設計で注意しなきゃいけないのは、スタックの巻き戻しを
している最中に呼ばれる例外クラスのコピーコンストラクタが例外を
発射しないことです。
この状況になると通常、terminate()直行で、どこのエラーで落ちたのか
もわからない結果になります。
それ以外の状況なら例外処理中に例外が発生しても対処は可能でしょう。