例外クラス

http://www.geocities.jp/ky_webid/cpp/language/025.html

throwには実質どんな型でも渡せるので当然自分で作ったクラスを渡すこともできます。

#include <iostream>
using namespace std;

class CError {
public:
    void print () { cout << "error" << endl; }
};

int main () {
    try {
        CError e;
        throw e;
    }
    catch ( CError& e ) {
        e.print();
    }
    return 0;
}
$ main
error

上記のように、例外用に用意したクラスのことを例外クラス言います。

また、catchではサブクラスで投げられた例外を、スーパークラスで受け取ることも可能です。

#include <iostream>
using namespace std;

class CError {
public:
    void print () { cout << "error" << endl; }
};

class CFooError : public CError {
public:
    void print () { cout << "foo_error" << endl; }
};

int main () {
    try {
        CFooError e;
        throw e;
    }
    catch ( CError& e ) {
        e.print();
    }
    return 0;
}
$ main
error

CErrorで受け取っているので表示されるのは「error」になります。

catchは上から順番に呼ばれていくので先にサブクラスを受け取るcatchを書いておけばクラス別に処理させることも可能です。

int main () {
    try {
        // 何かしらエラー
    }
    catch ( CFooError& e ) {
        e.print();
    }
    catch ( CError& e ) {
        e.print();
    }
    return 0;
}

このように、例外をクラスにすることで継承を利用した例外のツリーを表現できるので、throwは基本的に例外クラスに統一した方が綺麗且つ便利じゃないかなぁと思います。

最後に、catchで例外クラスを受け取るときにポインタではなく参照を使っているんですが、これにはわけがあります。

catchブロックの中で、キャッチしたオブジェクトのメンバにアクセスする必要がある場合、参照かポインタで受け取らないと、サブクラス側のメンバを正しくアクセスできません。なぜなら、CMathErrorクラスの実体(参照でもポインタでもない)として受け取った場合、それはCMathError型であり、CZeroDivideError型ではないからです(サブクラス側のメンバは見えない)。

また、実体で受け取る場合、throw されてくるのは「元の実体のコピー」ということになってしまいますから、無駄なコピーコストが発生します。ポインタの場合、例外オブジェクトを new で生成しなければならず、これをどうやって delete するのかという問題が起きます。これらを踏まえると、例外オブジェクトは参照で受け取るようにするのが最も適切な方法です。

という理由から例外クラスの受け取りは、実体やポインタではなく参照が使われているということです。