ProgrammingC++バックエンド開発者

C++における例外システムはどのように機能し、他の言語とどのように異なるのか?産業アプリケーションにおける例外処理をどのように設計するのが適切か?

Hintsage AIアシスタントで面接を突破

回答

C++における例外システムはtry-catchメカニズムとthrowキーワードに基づいています。例外的な状況(リソースエラーや不変条件の違反など)が発生した場合、tryブロック内でthrowオペレーターによって例外が発生します。適切なハンドラー(catch)を探す操作は、最初の一致する型までのスタックを示すことで行われます。

重要な詳細:

  • JavaやC#とは異なり、C++の例外は任意の型であることができます(通常はstd::exceptionを基にしたクラス)。
  • C++には例外の必須仕様(Javaのchecked exceptionsのようなもの)がなく、代わりにC++11以降ではnoexcept修飾子が導入されました。
  • 例外がスローされると、スローのポイントとハンドリングポイントの間のすべてのオブジェクトのデストラクタが呼び出されます — RAIIの維持が重要です。

設計:

  • 「意外な」エラーのみをスローし、その他のものにはリターンコードを使用してください。
  • 定数参照でキャッチする — catch(const std::exception& e)
  • GNUはポインタではなくコピー可能なオブジェクトをスローすることを推奨しています(ヒープメモリを避けるため)。

コード例:

#include <iostream> #include <stdexcept> void mayFail(bool fail) { if (fail) throw std::runtime_error("プロセスエラー"); } int main() { try { mayFail(true); } catch (const std::exception& ex) { std::cout << "キャッチした例外: " << ex.what() << std::endl; } }

トリックな質問

デストラクタから例外がスローされた場合、他の例外がスタックを介して上に伝播しているとどうなりますか?

回答: 例外処理中に新しい例外を生成することは許可されていないため(ダブル例外)、std::terminate()が呼ばれ、プログラムが異常終了します。スタックのアンワインディングが損なわれます。

例:

struct CrashOnDestruct { ~CrashOnDestruct() noexcept(false) { throw std::runtime_error("デストラクタでのエラー!"); } }; void func() { CrashOnDestruct obj; throw std::logic_error("エラー処理"); } int main() { try { func(); } catch (...) { } } // std::terminate()で終了します

テーマの微妙な点を知らないことによる実際のエラーの例


物語

大規模な金融アプリケーション内で、データベースのラッパークラスのデストラクタから例外がスローされていました。エラーが発生した場合の主要なトランザクションは例外を発生させ、アンワインド中にデストラクタが呼ばれ、再度エラーを投げました。アプリ全体が異常終了し、ユーザーの作業が失われました。開発者はデストラクタ内のthrowをログ記録と適切な処理に緊急で置き換えなければなりませんでした。


物語

マイクロサービスは、catch(std::exception)のように例外を処理していました。あるスレッドがユーザー定義型の例外(std::exceptionから継承されていない)をスローしていました。このようなエラーは捕捉されず、接続が予期せず切断され、メモリリークが発生しました。


物語

コンテナの移動メソッドでnoexcept修飾子が欠如していたため、標準ライブラリは高速なmove操作の代わりに遅いコピーを使用しました。パフォーマンスの重大な低下は負荷テストの時にのみ明らかになりました。