例外処理について – 固定ページ 2 – プログラミング – Home

通知
すべてクリア

[解決済] 例外処理について

固定ページ 2 / 2

aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

雑談板で長々とのたまっていたりしますが、こっちにもちょっとでしゃばってみる。

> マルチスレッドかつ例外をクリティカル以外で使うシステムでは「参照渡し」です。

なぜマルチスレッド云々が関わるかわからないあたり、まだ未熟な俺は、
もうしばらく考えねばならないようです。

> 更に、スレッドハンドラ関数のトップで(つまりスレッドの一番上の関数)
> catch(EXP_BASE *){}
> として、catch漏れを防ぎます。

これは、責任の所在を明確にすると言う俺の思想と反します。
catch すべきでないものは catch しない。
極論すれば、たとえそのためにプログラムが落ちたとしても。
落ちるようなことをする方が悪いのです。

ついでに言うと、今の仕事では「すべての関数に必ず例外ハンドラを設け、処理しない
例外は上に飛ばせ」という指令が下っていますが、これも俺の思想と反します。
処理せずに再スローするだけのハンドラはパフォーマンスの悪化にしかならず、
悪と断じます。

#思想に反するからといってコーディング規約に反してしまうわけには行かない
 のですが…

> コンストラクタでは既にクラス用のメモリが確保されていますので、その領域を
> 開放できません。

そでしたっけ?

> 参考までにoperator new(void*,size_t)です。

placement new っすか?

> スタック上に管理用クラスを宣言し、そのデストラクタで処理後処理を行います

このへんは「finally をサポートしてくれよぉ」ってとこですな。


返信引用
Bosscat
 Bosscat
(@Bosscat)
ゲスト
結合: 23年前
投稿: 73
 

もひとつ追記w

どんな時に例外を組み込むのか?
ですが、、

私はイベントループを持つフレームワークを組む場合に使います。
どこの誰だか解らないイベント処理ルーチンのエラーを一元的に管理する為イベントループの最
上位にcatchを置いて処理しちゃいます。

また、この様なシステムでは、致命的以外の用途で例外を使うので
例外を使える環境を完備して、多用途に使ってしまいます。(関数内gotoの代わりとか、、)


返信引用
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

書き忘れ

> 致命的例外でnewが成功する確率を考慮しています。
> 私は最低でも100byte以下にします

コンパイラはメモリ不足時の例外発生に備えて、あらかじめある程度のメモリを
予約し、例外クラスにはそれを解放して対処せよ、みたいな方針がありませんでした
っけ? 気のせいかもしんない。

その点、Microsoft はひどいもんですね。
「リソース不足であることを通知するメッセージをリソースからロードして表示する
ことは避けろ」
みたいなのをどこかで読んだ気がします。どこだか思い出せませんが。
(冤罪だったらゴメンね>MS)

また、メモリ不足で墜落しようが、メモリ不足を補足して不時着しようが、フライト
を続けることはできないわけですから、そんなに気にすることでもないような気が
します。
不時着することで乗員・乗客を何人救えるかにもよるとは思いますが。


返信引用
Bosscat
 Bosscat
(@Bosscat)
ゲスト
結合: 23年前
投稿: 73
 

>シャノンさん
>> マルチスレッドかつ例外をクリティカル以外で使うシステムでは「参照渡し」です。
マルチスレッドでは固定領域は必ず排他が必要です。例外オブジェクトそのモノの排他は難しい
です。

> catch すべきでないものは catch しない。
その通りだと思います。
が、例外系は「処理する人(責任者)を最初から設計に組み込むべき」では無いでしょうか?
何処かで処理される事を期待できないシステムは最初から間違っている気がします。

> 処理せずに再スローするだけのハンドラはパフォーマンスの悪化にしかならず、悪と断じま
す。
概ねその通りだと思います。
しかし、「例外処理がディスパッチャ化されている場合」には有り得ます。
つまり、始めから、再スローを想定されているモジュールです。
(ブラックボックス化の考え方で、こいつに投げときゃ、後は勝手にやってくれるだろ!って感
じで)
但し、私は好きでは無いし、推奨しません。
もう一つ、「パフォーマンスは事情が許す限り」だと思われます。
エラー系ならばトヤカク言われる事は少ないでしょう・・・

>> コンストラクタでは既にクラス用のメモリが確保されていますので、その領域を
>> 開放できません。
> そでしたっけ?
そうです!w
コンストラクタでメンバー変数に触れるでしょう?
そこからthrowされると、コンストラクタが完了しない->変数代入されない->開放できない。

>placement new っすか?
まさにその通り。。
スペルが自信なくて・・・w


返信引用
Bosscat
 Bosscat
(@Bosscat)
ゲスト
結合: 23年前
投稿: 73
 

う~む、、書き込みがクロスしてるなぁ~(時間的に)

メモリー不足時の動作ですが、私的には、落ちても仕方が無いと思います。
(基本的にnew,mallocのNULLチェック及び例外キャッチはしない主義です!)
但し、大容量の確保となると話は別で、その時はリトライ->ログ出し終了を行います。

この時大事なのは「ログ」です!
あくまで自衛ですが・・・


返信引用
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

> コンストラクタでメンバー変数に触れるでしょう?
> そこからthrowされると、コンストラクタが完了しない->変数代入されない->
> 開放できない。

確かに解放することはできませんが、勝手に解放されるんじゃないですか?
C++ はわざわざコンストラクタで発生した例外をキャッチするための機構を
用意してくれてるってのに、リークするんじゃあんまりです。
規格読んでないんで何とも言えませんが。

> この時大事なのは「ログ」です!
> あくまで自衛ですが・・・

フライトレコーダーですか。
録音内容から事故の原因がわかれば、たしかに改善されるかもしれませんね。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

>> マルチスレッドかつ例外をクリティカル以外で使うシステムでは「参照渡し」です。
マルチスレッドでは固定領域は必ず排他が必要です。例外オブジェクトそのモノの排他は難しい
です。

確かに例外的な事象以外に頻繁に利用するなら必ずコピーが発生してしまう
「実体渡し」は使いにくいかもしれませんから、delete が面倒なのは
トレードオフなのかもしれません。

ただ、言語的にマルチスレッドに対する定義が甘いこともありますし環境にも
よるのかも知れませんが、オブジェクトそのものを変更しない限り
参照だけならオブジェクト自体の排他制御は不要で、用途上、普通はまず
変更しないので特にその必要性がないものなのではないでしょうか。

私自身は実体を投げて参照で捕捉するようにしてますが、このように
Bosscat さんのいうところの「実体渡し」では投入されたオブジェクトは
他のスレッドから参照するのも難しい(意図的にやらない限り無理?)ですし。

> コンストラクタでメンバー変数に触れるでしょう?
> そこからthrowされると、コンストラクタが完了しない->変数代入されない->
> 開放できない。

これは、例外投入時点で未構築のメンバオブジェクトに対してデストラクタが
呼ばれないという話ですよね。((More) Effective C++ でしたっけ?)

初期化処理で全メンバオブジェクトを構築してしまえばコンストラクタ内でもちゃんと
メンバオブジェクトの方はデストラクタが呼ばれますし、new した生のポインタを
格納している場合などに問題になる(delte のタイミングがなかった)だけで、
auto_ptr なり shared_ptr なりでメンバに持てば、確保されている領域は
ちゃんとこれらのデストラクタで解放されますよね?
# 確か「確実に終了処理したいものはオブジェクトを作れ」とか<finally 代わり。

更に、初期化処理中に例外が投入された場合に備えて、コンストラクタ中で例外が発生して
しまったオブジェクト自体の後始末を可能にするために try-catch 構文も拡張されたと
記憶していますが(VC6とか使ってると恩恵が得られませんけど)、まだ他に問題は残っているの
でしたっけ。


返信引用
PAI
 PAI
(@PAI)
ゲスト
結合: 23年前
投稿: 359
 

CLASS::CLASS()
try : BASE(), a(), b()
{

}
catch(...){

}

メンバの初期化が投げた例外をとるのはこうやるんでしたっけ。


返信引用
Ban
 Ban
(@ban)
Prominent Member
結合: 5年前
投稿: 776
 

> メンバの初期化が投げた例外をとるのはこうやるんでしたっけ。

そうですね。
資源の管理について、
「スタック上に管理用クラスを宣言し、そのデストラクタで処理後処理を行います。」
を徹底している限り、あまり用途もなさそうですが。

実測したわけではありませんが「実体渡し」でほぼメンバのないオブジェクトが
コピーされるのと、「参照渡し」で new を行うと、主要コンパイラではどっちが
処理が重いんでしょうかね。

# って、続けるつもりなら別スレを起こすべきかな....。


返信引用
επιστημη
 επιστημη
(@επιστημη)
ゲスト
結合: 21年前
投稿: 600
 

僕は 実体渡し/参照受け だなー

try {
throw なにか;
} catch ( const なにか& ex ) {
// やべー
}


返信引用
aetos
(@aetos)
Noble Member
結合: 5年前
投稿: 1480
 

> コンストラクタでは既にクラス用のメモリが確保されていますので
ここからは、コンストラクトに失敗したクラスが解放できない、ように読めますが。

class CTest
{
public:
 CTest()
 {
  throw 0;
 }
};

int main()
{
 try
 {
  CTest *pTest = new CTest(); // コレ
  delete pTest;
 }
 catch( int )
 {
  // やべー
 }
}

CTest 用のメモリが確保されるけど、そのアドレスは pTest には入らない。
故に delete できないのでメモリリークする、と。最初はそう読めました。

> # って、続けるつもりなら別スレを起こすべきかな....。
雑談板にスレ立ってますんで、よろしければそちらで。


返信引用
Bosscat
 Bosscat
(@Bosscat)
ゲスト
結合: 23年前
投稿: 73
 

すみません。。嘘書いたと思うんで訂正だけ・・・
> シャノンさん
>CTest 用のメモリが確保されるけど、そのアドレスは pTest には入らない。
>故に delete できないのでメモリリークする、と。最初はそう読めました。
そう書きました・・・そして間違いです!

どうも、恥ずかしながら間違って覚えていたようです。

正解は Ban さんのおっしゃる通り
・変数代入されない。
・delete はされる!
・デストラクタは呼べない。
です。

やっばいな~今までの奴大丈夫かなぁ~。
まあ、元々使わない主義だし
どうしてもって時にはplacement new だったから大丈夫とは思うけど・・・
(恐らくoperator delete() に入っていただろうに、、なんでソコで気づかないかなぁ~)
--------------------------------------------------------------
ついでに、「実体渡し」と「参照渡し」の件
やっぱり実体渡しは人気有りますね。

まあ、Cマガジンかなんかで推奨していると聞いたんでその性もあるのかな?

私が実体渡しを避ける理由(まあこんなのは宗教みたいなモノだからこじ付け)ですが、、
「例外クラス自身にスマートポインタ(原因君,解決君)を持たせる」場合があるからです。
(
解決君」はカテゴリを持っていて、エラー復帰系を特定できる。
ループでcatch後は、エラー復帰部に原因君と解決君を投げておしまい。
)
これを実体渡しすると、operator=の実装が必要なのです。→面倒くさい!

とまあ、大した理由では無いですね。。。


返信引用
Takahashi
 Takahashi
(@Takahashi)
ゲスト
結合: 21年前
投稿: 45
Topic starter  

# うはー・・・すごいレスついてる・・・
# それだけ皆さん例外に対して関心が強いということですね。
# 例外、恐るべし・・・

クリティカルな例外しか使わないと思いきや(またも思いこみ・・・)
Bosscatさんの様な方法で例外を積極的に使う方法もあるのですね、

> class EXP_BASE{};
> class EXP_WARNING:public EXP_BASE{};// 警告
> class EXP_ERROR:public EXP_BASE{};// エラー
> class EXP_FATAL:public EXP_BASE{};// クリティカル

軽微な例外から、重傷まで場合分けして例外処理するというのは興味がわきます。
ただ、例外をクリティカルなエラー以外の用途で使用するのは、いろいろと面倒が
多いということもあるのですね。(勉強になります)
私の場合、簡単なプログラムしか作らず、また複数人での開発などは行っていないので、
やはり例外は致命的なエラーの通知に限って使用すると思います。
何せお手軽簡単ですので・・・(^_^;

自動処理の徹底については、auto_ptr 等が利用できればそれらを使用するように
しています。それ以外の場合にもなるべくラップして使用する様にしています。
(管理が楽ですものね)

実体か参照渡しかについては、初めは MFC が参照渡しなのでまねをして
参照渡ししていたのですが、やはり、管理が簡単なので実体渡し参照受けを
使用しています。

# 以前そっちの方がよいと忠告されたことがあったけど、ネタはCマガですか・・・
# 皆さん物知りですね。というか私の場合、勉強不足だ・・・あうう


返信引用
固定ページ 2 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました