C++/STLの例外

C++の例外も、やはり避けて通っていたのだが、ちょっと調べて見ることにした。
以下、Visual C++ .NET 2003の調査結果の覚書。

例外をcatchしないとどうなるか?

例外をcatchしないとVC++.NET2003(おそらくそれ以降でも)
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
と出る。デバッグ版のライブラリをリンクしていると上記メッセージの前にダイアログが出てそのままデバッグできる。

C++の例外ってどういったものがあるか?


bad_alloc new演算子等でメモリを確保できないときに発生。vector等のコンテナでもメモリ確保に失敗すると発生する。
bad_cast dynamic_castキャストに失敗したときに発生、参照型で発生する。ポインタ型では発生しない。ポインタ型のキャストに失敗すると0(ヌルポインタ)が返る。
bad_typeid typeid演算子で無効なオブジェクトを渡すと発生する。
bad_exception 例外仕様に反した例外が発生した場合、プログラムは強制終了(terminateの呼び出し)されるが、bad_exceptionに変換する作法(?)がある。VC++ .NET 2003では正常に変換できなかった。


logic_error プログラムの論理エラー(回避可能なエラー)、以下のエラーの親(スーパークラス)になっている。
  
  • domain_error 領域エラー、VC++.NET 2003のSTLでこのエラーがスローされることはない模様。
  • invalid_argument 不正な引数、bitsetの初期化エラーでスローされる。
  • length_error 長さエラー、コンテナ(vector,deque,list,string等)でmax_size以上の領域を確保(reserve,insert)しようとしたら発生。max_sizeメソッドは各コンテナで扱うことの出来る要素の最大数を指す、実際に確保できるサイズはこの値未満になる。
  • out_of_range 範囲外、コンテナ(vector,deque等)のatメソッドの範囲チェックに引っかかった。

runtime_error 実行時エラーの総称、localeクラスのcombineテンプレートメソッドでスローされる。以下のエラーの親になっている。
  • underflow_error アンダーフロー、VC++.NET 2003のSTLでこのエラーがスローされることはない模様。
  • overflow_error オーバーフロー、bitsetのto_ulongメソッドでスローされる。
  • range_error 範囲エラー、VC++.NET 2003のSTLでこのエラーがスローされることはない模様。
  •    
  • ios_base::failure iostream関連の例外、basic_ios::exceptionsメソッドにより例外の発生をコントロールできる。


例外のサンプルコード

以下、各例外を検証するコードです。
#include <iostream>
#include <string>
#include <new>
#include <eh.h>

using namespace std;


/**********************************************************************
    bad_alloc
**********************************************************************/
int test_bad_alloc()
{
    int ret = 0;

    try {
        int *ptr = new int[511*1024*1024UL];
        if ( ptr == 0 ) {
            ret = 1;
        }
    } catch ( bad_alloc e) {
            ret = 2;
    } catch ( ... ) {
            ret = 3;
    }
    return ret;
}

/**********************************************************************
    bad_cast
    VC 2003 ランタイム型情報を有効にしないとコンパイルできない
**********************************************************************/
    class base_class {
        public:
        virtual void vfunc() const {}
    };

    class child_class: public base_class {
        public:
        virtual void virtualfunc() const {}
    };

int test_bad_cast_ref()
{
    int ret = 0;
    base_class base;
    base_class &ref_base = base;
    try {
        child_class& ref_child = dynamic_cast<child_class&>(ref_base);
        ret = 1;
    } catch (bad_cast) {
        ret = 2;
    } catch ( ... ) {
        ret = 3;
    }
    return ret;
}

int test_bad_cast_ptr()
{
    int ret = 0;
    base_class base;
    base_class *ref_base = &base;
    try {
        child_class *ref_child = dynamic_cast<child_class*>(ref_base);
        ret = 1;
    } catch (bad_cast) {
        ret = 2;
    } catch ( ... ) {
        ret = 3;
    }
    return ret;
}



int test_bad_typeid()
{
    int ret = 0;
    base_class  *b = 0;

    try {
        const type_info &t = typeid(*b);
        cout << t.name();
        ret = 1;
    } catch ( bad_typeid ) {
        ret = 2;
    }
    return ret;
}

/**********************************************************************
    bad_exception
**********************************************************************/
void func() throw()
{
    // warning C4297が出る(わざと例外仕様に反した例外を出す)。
    throw "bad_exception!";
}

void convert_unexpected()
{
    throw ;
}

int test_bad_exception()
{
    int ret = 0;

    // VC++ .NET 2003 SP1 で最適化を掛けるとconvert_unexpected関数が呼ばれない
    // VC++ .NET 2008 Express Edition でも同様
    unexpected_function back = set_unexpected(convert_unexpected);

    try {
        func();
        ret = 1;
    } catch ( char * ) {
        ret = 2;
    } catch ( bad_exception ) {
        ret = 3;
    } catch ( ... ) {
        ret = 4;
    }

    set_unexpected(back);
    return ret;
}

/**********************************************************************
    null pointer assigment
**********************************************************************/
int test_null_pointer_assigment()
{
    int ret = 0;

    // VC++ .NET 2003 SP1 で最適化を掛けるとcatch(...)に行かない。
    // _set_se_translatorを使って構造化例外処理からC++例外処理へ
    //  例外をコンバートできる
    try {
        char *ptr = 0;
        *ptr = '\a';
        ret = 1;
    } catch ( ... ) {
        ret = 3;
    }
    return ret;
}

int main(int argc, char* argv[])
{
    cout << "test_bad_alloc:" << test_bad_alloc() << endl;
    cout << "test_bad_cast_ref:" << test_bad_cast_ref() << endl;
    cout << "test_bad_cast_ptr:" << test_bad_cast_ptr() << endl;
    cout << "test_bad_typeid:" << test_bad_typeid() << endl;
    cout << "test_bad_exception:" << test_bad_exception() << endl;
    cout << "test_null_pointer_assigment:" << test_null_pointer_assigment() << endl;
    return 0;
}

コンパイル、実行結果

C:\>cl /GR /GX cpp_exception.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 13.10.6030 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.

cpp_exception.cpp
cpp_exception.cpp(98) : warning C4297: 'func' : 例外をスローしないはずだがそれを

する関数。
        __declspec(nothrow) または throw() が関数で指定されました。
Microsoft (R) Incremental Linker Version 7.10.6030
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cpp_exception.exe
cpp_exception.obj

C:\>cpp_exception
test_bad_alloc:2
test_bad_cast_ref:2
test_bad_cast_ptr:1
test_bad_typeid:2
test_bad_exception:2
test_null_pointer_assigment:3
2008-04-30 | コメント:0件

コメントをどうぞ


『不適切なコメントへの対応』を一読下さい。
現在コメントは承認制となっております。

Previous Page | Next Page