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

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

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

11回答

3975閲覧

例外処理が存在する意味

apa

総合スコア68

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

7グッド

10クリップ

投稿2020/07/29 10:05

effective C++などを読んでいると例外処理について多く触れられていますが、
そもそも例外処理はコードのバグなのであって対処するコード(try catch)がなぜあるのかがわかりません。
例外がでたらそこで直せばいいだけですし
例外ありきのコードは成立していないと思います。
例外処理について調べても上記についての説明がなされていないものでしたので
この例外処理を回避するコードがなぜ存在するのか教えていただきたい。

GqewG9aG7S, q_sane_q, bracket_i, nagasato, episteme, tiitoi👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2020/07/29 10:37

”いい質問"ですねー、高評価。
guest

回答11

0

例外の意味を誤解しています。バグを拾う機構じゃないです。

関数やメソッドを呼び出した場合、
・成功と失敗を戻り値で返す
・戻れば成功で、失敗は例外発生で知らせる
という2通りの考え方があります。

例えばCでは前者の考え方なので、

C

1if((fp=fopen(fname,"r"))==NULL){ 2 perror("fopen 1"); 3 exit(1); 4}

のように呼び出す都度常にエラーチェックが必要です。
あるいは、「割り算する前に分母が0でないか確認してから割り算すべき」という考えに立てば、fopenする前に別の関数でファイルの存在チェックやアクセス権チェックをしてからfopenすべきかも知れません。

ディスクフル(やネットワークドライブのネット障害)を心配すると、fopen(fname,"w")が成功してもその後のfprintfputcが失敗するかも知れず、これら呼び出しの全てに返り値チェックが必要です。printfでもリダイレクトされている可能性があるので同様。
十年くらい前(?)ですが、awkでこのチェックが漏れていて、ディスクフルでも正常終了するというバグ情報を見ました。

こういう毎回の事前チェックや返り値チェックが煩わしいという考えに立って設計されたのが例外機構のある言語です。
本筋のロジックの流れが見やすいように書いて、本筋で無い処理は隅っこに書くという考え。

Cで実用的なプログラムが書けているわけで、すべて事前チェックもしくは返り値チェックを徹底すれば例外機構なしでも実用的なプログラムが書けます。ただ、それが読みやすいかどうかですね。
awkのようなメジャーなツールですら徹底できていなかったという事実も受け止める必要があります。

投稿2020/07/29 15:14

otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

プログラム動作意図から外れた状況に対処するという意味の「例外処理(エラーハンドリング)」と、プログラミング言語C++の機能として提供される 「例外機構(throw文とtry/catch構文)」 を区別して認識する必要があります。

C++に限らず、例外処理(エラーハンドリング)が必要になる状況は2つに分類できます:

  • [A] プログラマのコード実装誤り: ヌルポインタ参照はがし、配列範囲外アクセス、未初期化変数からの読取りなど。
  • [B] プログラム実行時の外部環境: ファイル不在、ネットワーク不達、ディスクFULLでファイル書き込めない、メモリ不足など。

例外がでたらそこで直せばいいだけですし

プログラマの責任範囲において直せるのは、[A] に対する例外処理(エラーハンドリング)のみです。

むしろプログラミング言語C++に限れば、[A] に対する例外機構はほとんど期待できません。例:ヌルポインタ参照はがしはC++例外機構を用いても検知できません。運が良ければ該当箇所でプログラム異常停止、運が悪いとランダムにデータ破損したり一見無関係な箇所でプログラム異常停止します。
他のプログラミング言語(例えばJavaやC#)ではヌルポインタ/参照はがしのようなロジックエラーでも、各言語における例外機構によって検知可能です(NullPointerExceptionNullReferenceException )。

例外ありきのコードは成立していないと思います。

実用的なプログラムであれば、[B] に対する例外処理(エラーハンドリング)を行うべきです。

プログラミング言語Cには例外機構が存在しないため、例外処理(エラーハンドリング)は「特殊な戻り値 や エラーコード」を用いて実現されます。
例:fopenでファイルオープンに失敗すると関数は値NULLを返し、エラー原因を表すコードをerrno変数に格納する。

プログラミング言語C++の場合、ほとんどの標準ライブラリ機能は「例外機構」の存在を前提に設計・提供されます。サードパーティー製ライブラリでも、多くは標準ライブラリに準じた設計が行われます。
例:new MyClass()でメモリ確保に失敗すると、std::bad_alloc例外を送出する。

つまり実用的なC++プログラムを作るとなると、例外機構(throw文とtry/catch構文)を用いた例外処理(エラーハンドリング)を避けることはできません。


C++公式サイト(isocpp.org)のFAQ項目では下記ように説明されています。太字部は回答者による強調。括弧内は参考訳。

Why use exceptions?
(どうして例外を使うのか?)

What good can using exceptions do for me? The basic answer is: Using exceptions for error handling makes your code simpler, cleaner, and less likely to miss errors.
(例外の利用にはどんな利点があるでしょう?基本的な回答は: エラーハンドリングに例外を使うことは、あなたのコードを単純、簡潔にし、そしてエラーが起きにくくなります。
But what's wrong with "good old errno and if-statements"? The basic answer is: Using those, your error handling and your normal code are closely intertwined. That way, your code gets messy and it becomes hard to ensure that you have dealt with all errors (think "spaghetti code" or a "rat's nest of tests").
(では「古き良き error と if文」 の何が問題なのでしょう?基本的な回答は: それらを利用すると、エラーハンドリングと通常コードが密接に結び付いてしまいます。そこでは、あなたのコードはひどく散らかってしまい、あらゆるエラーの取り扱いを強制することを困難にします("スパゲッティコード" や "ネズミ捕り・テスト" を想像してください)。)

First of all there are things that just can't be done right without exceptions. Consider an error detected in a constructor; how do you report the error? You throw an exception.
(まず第一に、例外なしには正しく処理できないケースが存在します。コンストラクタ中でのエラー検知を考えてください;どうやってエラーを報告しますか?例外を送出します。
That's the basis of RAII (Resource Acquisition Is Initialization), which is the basis of some of the most effective modern C++ design techniques: A constructor's job is to establish the invariants for the class (create the environment in which the member functions are to run) and that often requires the acquisition of resources, such as memory, locks, files, sockets, etc.
(これはRAIIの基礎であり、とても有用なモダンC++設計テクニックの基礎となっています: コンストラクタの仕事はクラスの不変条件を確立する(メンバ関数を実行する環境を整える)ことであり、しばしばメモリ/ロック/ファイル/ソケットなどのリソース確保を要求します。)

<以下略>

投稿2020/07/30 05:32

編集2020/07/30 06:09
yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apa

2020/07/31 09:24

返信ありがとうございます。 ファイル不在、ネットワーク不達、ディスクFULLでファイル書き込めない に関してはどういったプログラムか想定がつくのですが、 メモリ破壊を探知 例外を吐かせようとすると 巨大なクラスのメモリ確保時のみに適用するか 例外対応クラスを作って適用させるかっみたいになるのでしょうか。 ただ例外に関しては投稿を読んでほぼ理解できたと思います。 ありがとうございました!!(質問に答えてくださったすべての人へ)
yohhoy

2020/07/31 14:50

> メモリ破壊を探知 例外を吐かせようとする C++言語の場合、これは実現不可能です。 メモリ破壊を行うような操作は、そもそもC++例外機構では検知できません。 コードレビューや静的検査ツール、デバッグ実行&デバッガを用いて問題を検出し、コードを修正する必要があります。
sample__

2020/07/31 15:21

よこからすみません 今回の質問の"例外"というのは一般保護例外・ページフォルト例外などの例外のことですか? それよりも上の層での例外の話?
yohhoy

2020/08/04 08:52 編集

質問にC++タグ、本文でも Effective C++ (https://www.maruzen-publishing.co.jp/item/b294734.html ) に言及されていますから、C++例外を対象とした回答としています。 (一般保護例外やページフォルトといった、低レイヤな話題はこの回答では取り扱っていません)
guest

0

そもそも例外処理はコードのバグなのであって

この考え方が妥当ではありません。「通信が切断された」ような状況はプログラムにバグがなくても発生します。

投稿2020/07/29 10:29

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apa

2020/07/29 13:41

返信ありがとうございます。 確かにネットワークには使えるかもしれないですが (自分は簡単なネットワークプログラムしか組んだことはないです) 自分でネットワークコードを組む分にはほとんどif文で回避できると思います。
maisumakun

2020/07/29 14:01 編集

では、「メモリ不足」だとどうでしょうか。もちろん、動的確保されるメモリが足りるかを、事前にチェックすることは一般に不可能です。 C++でvectorやstringを使う場合、使う途中でもシームレスにメモリ確保が必要となる場面がありますので、逐一チェックすようとすればあちこちにチェックコードが必要となって、本筋のコードはすっかり埋もれてしまうことでしょう。
fana

2020/07/29 15:10

> if文で回避できると思います だから? 「AでもBでもCでも何かができる(と思える)」ときに,自分はAでやるからBやCの存在が許せないとかそういう話?
m.ts10806

2020/07/29 20:30

アプリケーション組んだ側が想定できない異常事態=例外 で済む話じゃないでしょうか。 ユーザーから何かしら入力を受け付ける場合、必ず想定どおりの情報を送ってくるわけではないですし、DBなどアプリケーション外の仕組みを利用する際は直接拾うことはできません。 全ての例外を事前に想定してifやなんやで対処しきるのは無理な話ですよ。
episteme

2020/07/29 22:35

「if と goto さえあればどんなコードも書けるのに while とか switch とかがなぜあるの?」 に近いのかな...
guest

0

例外機構がない場合、返り値で処理の成功を判断することになります。確かにそういう考えの言語はあります、c言語とか。

そういう言語で例えば、
「処理A,B,C,Dを順番に行う、ただし各処理が失敗した時点で以降の処理は実行せず代わりにEを行う」
という処理を書こうとしたらif文が4つ並んでしまいます;

if(!A()){ E(); return; } if(!B()){ E(); return; } if(!C()){ E(); return; } if(!D()){ E(); return; }

他にも色々書き方はあるでしょうが、少なくとも「A,B,C,Dの順番で実行する」という本題からかなり余計なコードが増えることでしょう。

これが例外機構があればtry-catchですっきり書けます

try { A(); B(); C(); D(); } catch { E(); }

投稿2020/07/29 15:00

編集2020/07/30 04:26
ozwk

総合スコア13553

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

例外ありきのコードは成立していないと思います。

んなこたーない。

何らかの異常に対処できる(が検出はできない)関数が
何らかの異常を検出できる(が対処はできない)関数を 直接/間接的に呼んでいるとき、

その両者間で何らかの異常を伝達するスマートなやりかた が例外です。

※ バグで停止するんなら assert でもカマしておけばいいので。

投稿2020/07/29 10:34

編集2020/07/29 11:14
episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

こんにちは。

そもそも例外処理はコードのバグなのであって対処するコード(try catch)がなぜあるのかがわかりません。
例外がでたらそこで直せばいいだけですし
例外ありきのコードは成立していないと思います。

2つの点で間違っていると思います。
まず、例外処理はバグ検出時にも良く使いますが、バグ以外のケースでも有用です。
次に、バグをなくすことは不可能ですのでバグ検出時の処理をしないのは危険過ぎます。めったに発生しない筈のバグ対処に例外機構を使うのは結構好ましい使い方と思います。

ところで、もしかしてバグのないプログラムが存在すると考えているのでしょうか?
↓がわかり易いです。短いので一読することをお勧めします。
バグのないソフトウェアの作れない理由

投稿2020/07/29 15:49

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

例外など使わずに、関数やメソッドがエラーを戻り値で返して、それをチェックすればいいじゃないか、と考えていらっしゃるかもしれません。しかし、それがきれいに出来ない状況がたくさんあるのです。

例えば、コンストラクタは戻り値を返すようにはできていません。コンストラクタは、テンポラリ・オブジェクトの生成や引数を渡すときなど、戻り値がチェックできないような場所でも頻繁に使われるからです。オーバーロードした演算子も同様です。しかし、コンストラクタや演算子でもエラーは発生することがあります。そのエラーをクラスの利用者に通知する必要があります。

また、vectorなどのコンテナは、その中身がどんなエラーを返すか事前に知ることはできません。vectorがサイズを変更しているときに、中身で起きたエラーを、vectorのコードを飛び越して利用者に通知するメカニズムが必要です。

例外は、エラーを抽象化して正常処理から分離することにより、正常処理のコードをわかりやすく、しかも再利用可能な形で書けるようにしているのです。例外がなければ、C++の抽象化をする能力は中途半端なものになり、「だったらCで十分だよね」ということになっていたと思います。

投稿2020/07/29 13:59

Bearded-Ockham

総合スコア430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「例外がでたらそこで直せばいい」のとは違います。通信の応答が規定時間内になかったなどは回線の状態によっては頻発することが有ります。・・・1μ短縮出来たら受けられた・・・かも知れない・・・で1μ短縮したがダメだったw・・・などなど、きりがありません。
“人間は、間違いを犯すもの”です。・・・そして、通常運用されるプログラムは、少なくとも数万行とかのレベルです。“そこ”を治すたには、他に影響が出ないか細心の注意が必要です。そのために、想定される問題を先取り(考慮)して、例外処理を入れるのです。
逆に『考慮ミスは。立派なバグです』

投稿2020/07/29 10:57

cateye

総合スコア6851

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apa

2020/07/29 13:45

デバッグめんどくさいから あらかじめやっておこー みたいなことなんですかね? 例外処理がおきてたら [A地点でばぐ] みたいにテキストか通知だして そこを直すみたいな感じで となると例外処理はデバッグ機能の一種ということなんでしょうか
cateye

2020/07/29 14:58 編集

いや違います、例えば通信の例であれば、タイムアウト時に要求内容次第で「再送要求を出すとか、受信処理を破棄する」とか、そういった判断をするなど、前向きwな姿勢です。 ・・大事な事は、(設計者やプログラマが)事前に(外部要因等により)“それ”が起こる事を理解(想定)していたか?と、言うことです。従って、デバッグみたいに、事が起こってから対応するのとは違います。
guest

0

ちょっと何言ってるかわかりません.
一体何をもって「コードのバグ」だと言っているのか?

例外という機構があります.throwとtryとcatchという仕組みが.
→それを何に使うかはプログラマ次第です.

投稿2020/07/29 10:23

fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/07/30 01:43

例外=バグ ではない. バグったコードが動作した結果として例外を引き起こすことは有り得るが,そんなコードを書いた人も想定外な何かをこっそり無かったことにして強引に動作を続行させるためにcatchが存在するわけじゃない. ある関数で 有り得る/起こり得る 処理の失敗が発生したときにどうするか? という場面では ・戻り値でエラー情報を返す ・追加の引数を受けておいてそこにエラー情報を突っ込む ・どこか別の場所にエラー情報を書くからそれを別途チェックしてね,という話にする ・例外を投げる ・etc いろいろな方法が考えられ,それぞれちょっとずつ何かが違う. どれを採用するかがその関数の仕様となる.使う側はその仕様に合わせて使う.それだけ. (関数の仕様が「戻り値」なら,使う側は戻り値をチェックするかもしれないし,「例外」ならtry/catchを書くかもしれない)
fana

2020/07/30 01:53

もちろん,処理中に(何も問題が発生してないけど)唐突に例外を投げることも可能. つまり,例外機構をgotoのような何かのように利用することも「可能」. (この場合,例外は"バグ"とは無関係だよね.処理フローの実現のための道具として書かれているだけなので) 実際にそういう目的に例外機構を使っちゃうかどうかetcはプログラマの思想やら何やら次第となる. (なので,異なる思想の持ち主との間で戦争が起き得る)
guest

0

例外が必要か? という話であれば絶対に必要というわけではありません
いろいろな言語が存在していますが、
「こんな機能があった方が書きやすいのでは」
「こういう仕様の方が安全なのでは」
と、それぞれの考え方によって設計されています
その中で
例外みたいな機能あった方がいいよね、と考えた人達が居たわけです
例)自分でメモリ開放するよりGCに任せた方が安全だよね

さてその例外についてですが
他の方も回答されているように例外発生=バグではありません

例えば指定されたファイルを読み込んで、その中の値を使って何か処理をする
というプログラムを考えたとき
正常にファイルが読み込めない可能性があります
HDDが故障しているかもしれないですし誰かが外付けHDDのコードに脚を引っ掛けるかもしれないです
こういったドライブ側の都合でファイルが読めないのは「プログラム上のバグ」とは言えないですよね

しかしいずれにしてもファイルは正常に読めなかったのだから以降の処理は行なえません
この「ファイルは正常に読めなかった」問題に対応するのはプログラム側の仕事です
ここでファイルを開く関数が結果コード(開けたら1、開けなかったら0とか)を返してそれをifで判定しするという言語仕様でも可能ですが、
開けなかったら問題の情報を詰めた例外投げてcatchまですっ飛ばしてしまおう、という思想の言語もあるということです。

仮に
「開くファイルが3つあってそのどれか1つでも開けなかったら以降の処理はできない、ファイルが開けなかったことはコンソールに出力してね」
だったらどうでしょうか
3つそれぞれにifを噛ませるより、どのファイルからでも同じcatch節に飛ばす方が処理がスッキリして見えないでしょうか

投稿2020/07/30 03:19

q_sane_q

総合スコア610

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

q_sane_q

2020/07/30 05:34

これは余談ですがJavaには検査例外なんてのもありましたね。 ファイルアクセスやDBアクセスなど発生するまでプログラム上で検知できないタイプの例外はtry-catchしないとコンパイルできなくなるJavaらしい荒っぽいやつです。
guest

0

バグではありません。仕様です。
違った。
例外が発生することを仕様に盛り込むことで、柔軟に対応できる様になります。

ユーザーが使うアプリが例外メッセージをそのまま出すのはバグと言ってもいいでしょう。
ユーザーが使うアプリを作る人には、例外として通知します。そうすることで、エラーとして終わる様な処理をすることと、なんらかの対策をしてリカバリーできることを選ぶことができます。
もちろん、戻り値として通知することもできますが、例外として通知すると、呼び出しの深い階層から一気に戻ることや、エラー処理を1箇所にまとめて記述することができる様になります。

投稿2020/07/29 10:14

Q71

総合スコア995

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apa

2020/07/29 13:37

リカバリーできるといっても throwは例外がおきそうなところにおくものなので、 ここで例外発生しそうだなーと プログラマーが理解していることになります ならそこをデバッグすればいいだけじゃないか? とおもってしまっています
maisumakun

2020/07/29 13:58 編集

> ならそこをデバッグすればいいだけじゃないか? 極端な例ですが、すでに「例外を投げる」形で実装されているライブラリを使う場合、その例外を受け取らなければプログラム全体が落ちてしまいます。 通信・メモリ・ファイルなど、リソースに絡む例外には事前チェックできないものも多々ありますので、そのような状況で例外を投げてくる場合は、それを処理する以外の選択肢はありません。
pepperleaf

2020/08/01 05:23

> 例外として通知すると、呼び出しの深い階層から一気に戻ること ある意味、これプログラマーの手抜き。この手の大域脱出は、後からの追跡を困難にして、デバッグを難しくしてます。(いま、苦闘中)
Q71

2020/08/01 10:46

確かに手抜きですね。 ファイルを読もうとしたが、そのファイルがない(ユーザーの入力間違い)、ファイルが使用中で読めない、ファイルの読み取り権がない、ということは普通にあり得ます。こういう実行時にしかわからない例外的状況をどう伝えるか、そもそも例外機構として組み込んでいる時点で「想定された例外」ではあります。それを後ろの工程に後始末させようとしているわけですから、手抜きと言えるでしょう。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問