exception class。
例外処理(C++)で送出するために設計されたクラス型(C++)。
概要
intやconst char*のような基本型を例外として送出することもできるが、それだけでは何が起きたのかが曖昧になりやすい。
例外クラスを使うと、
- 何の種類の例外かを型で区別できる
- エラーメッセージなどの追加情報を持たせられる
- 例外ごとに異なる
catchで処理できる
例
class ArrayException
{
private:
std::string m_error{};
public:
ArrayException(std::string_view error)
: m_error{ error }
{
}
const std::string& getError() const
{
return m_error;
}
};if (index < 0 || index >= getLength())
throw ArrayException{ "Invalid index" };このように、例外クラスを送出すると、どのクラスから発生したどの種類の異常かを表現しやすくなる。
受け取り方
クラス型の例外は、通常const参照で受け取る。
catch (const ArrayException& exception)
{
std::cerr << exception.getError() << '\n';
}これは不要なコピーを避けるためであり、オブジェクトスライシングを防ぐ意味もある。
ポインタで受け取る方法は、特別な理由がない限り通常は使わない。
継承との関係
例外クラスも通常のクラスと同じように継承(C++)できる。
そのため、基底例外クラスを作り、その派生例外クラスをまとめて扱うことができる。
class Base {};
class Derived : public Base {};
try
{
throw Derived{};
}
catch (const Base&)
{
std::cerr << "caught Base";
}
catch (const Derived&)
{
std::cerr << "caught Derived";
}この例ではDerivedが送出されても、先にcatch (const Base&)が一致するため、Derived用のcatchには到達しない。
そのため、派生例外クラス用のcatchは、基底例外クラス用のcatchより先に書く必要がある。
std::exception
標準ライブラリの多くの例外クラスは、<exception>ヘッダのstd::exceptionを基底クラスとしている。
そのため、
catch (const std::exception& exception)
{
std::cerr << exception.what() << '\n';
}のように書くことで、標準ライブラリ由来の多くの例外をまとめて受け取れる。
std::exceptionには仮想関数what()があり、例外の説明文字列を返す。
多くの派生例外クラスはこのwhat()をオーバーライドしている。
独自例外クラスをstd::exceptionから派生させる
独自例外クラスもstd::exceptionやstd::runtime_errorから派生させることができる。
class ArrayException : public std::runtime_error
{
public:
ArrayException(const std::string& error)
: std::runtime_error{ error }
{
}
};std::runtime_errorを使うと、エラーメッセージの保持やwhat()の実装を自分で書かずに済む。
一方、std::exceptionから直接派生する場合は、必要に応じてwhat() const noexceptをオーバーライドする。