[セットトップ]C++のexception


本稿では、C++の異常に関するすべてのことを説明します.
異常を解析関数から絶対に逃がさない
EXceptionが構造関数から逃れるのを阻止するのは、主に2つの原因です.1異常処理中のスタック展開動作を防止するとterminate関数が呼び出されます.プログラムは終了し、実際にはエラーがそれほど深刻ではない場合があります.
[挿入:terminate関数はいつ呼び出されますか?][回答:By default,the terminate handler calls abort.But this behavior can be redefined by calling set_terminate.
This function is automatically called when no catch handler can be found for a thrown exception, or for some other exceptional circumstance that makes impossible to continue the exception handling process.//1つの例外に対応するcatch文ブロックがない場合、terminate関数が自動的に呼び出されるか、処理できない例外がある場合.
This function is provided so that the terminate handler can be explicitly called by a program that needs to abnormally terminate, and works even if set_terminate has not been used to set a custom terminate handler (calling abort in this case).
terminateが呼び出された場合:
1異常が送信され、コンストラクション関数に異常が発生する場合2異常が送信された場合、または、コンストラクション関数に異常が発生した場合3静的オブジェクトのコンストラクションまたは、コンストラクション関数に異常が発生した場合5異常をカスタマイズし、しかし、実際に異常が発生していない場合6デフォルトのunexcepted()関数を呼び出す場合(unexcepted関数は、この関数が予期せぬ異常を投げ出したため)
2 destructorが完了すべきすべての動作を完了することを保証するのに役立ちます.
#include <bits/stdc++.h>
using namespace std;

class myexception{};

class Session
{
public:
    Session()
    {
        logCreation();
    }

    ~Session()
    {
        try{ //   try catch      
            logDestruction();
        }catch(...){
            cout << "catch exception..." << endl;
        }
    }

private:
    static void logCreation(){cout << "enter..." << endl;}
    static void logDestruction() {cout << "out..." << endl;throw myexception();}
};

int main()
{
    Session s;
    return 0;
}

catchのサブステートメントブロックVS.関数の呼び出し
3種類の違い:
1 exceptionオブジェクトは常にコピーされます.by valueで2回コピーする場合もあります. 
2 exceptionのオブジェクトとして投げ出され、許可される変換操作は「関数に渡される」よりも少ない.
3 catch句は、関数呼び出しの「best fit」のポリシーとは異なり、出現する順序でマッチングされます.すなわち、「first fit」のポリシーです.
同じ点:関数パラメータとexceptionの伝達方式は3種類あります:by value,by reference,by pointer,異なる点:関数を呼び出すと、呼び出しが完了すると、制御権は最終的にその関数の呼び出し側に戻ります.しかし、異常を投げ出すと、制御権は決して投げ出しに戻らない.
異なる点:関数のバインド方法.値が伝達されている場合は、オブジェクトのコピーが伝達され、参照されている場合は、直接オブジェクトにバインドされます.しかし、これは異常に対して間違っています.あなたの異常なcatchのパラメータが値を伝達しても参照を伝達しても、catchブロックに渡されたものはすべてオブジェクトのコピーです.(なぜかというと、制御権がこの関数から離れると、その関数のすべての局所オブジェクトが生存空間から離れて自動的に解析されるため、このときcatchブロックに渡されるオブジェクトはすでに解析されたオブジェクトであり、これは明らかに間違っている.この一時オブジェクトの生存期間がこの関数に限られていなくても、この結論は依然として成立する).ここでの「exceptionオブジェクトは常にレプリケーションを引き起こす」という動作は、関数呼び出しよりも例外処理が遅くなることが多い!!!オブジェクトレプリケーションがexceptionとして扱われる場合、レプリケーション動作はオブジェクトのコピーコンストラクション関数によって実行され、このコピーコンストラクション関数はオブジェクトの静的タイプに対応します!!ダイナミックタイプではありません!!例えば・例えば次のコード:
class Base{
public:
    Base(){cout << "ctor in Base..." << endl;}
    Base(const Base &){cout << "copy ctor in Base..." << endl;}
};

class Derived : public Base {
public:
    Derived(){cout  << "ctor in Derived..." << endl;}
    Derived(const Derived &b):Base(b){cout << "copy ctor in Derived..." << endl;}
};

void f()
{
    Derived d;  // print : ctor in Base  ctor in Derived
    Base &b = d;
    cout << "throw..." << endl;
    throw b;  //copy ctor in Base
}

void test()
{
    try{
        f();
        cout << "never be here..." << endl;
    }catch(Base) //      Base,  print: copy ctor in Base,   Base reference,         
    {
        cout << "catch..." << endl;
    }
}

int main()
{
    test();
    return 0;
}

「exceptionオブジェクトが他のオブジェクトのコピーである」ということは、上から知っていますが、ここでは異なるcatchブロックでexceptionをどのように渡すかという問題が発生します!!!
以下のthrowとthrowは、catchブロックにおける現在のexceptionの伝達を継続する動作をそれぞれシミュレートする.
//  throw         exception,      throw,            exception     ,(test1  throw)
//     throw + catch     ,          。

class Base{
public:
    Base(){cout << "ctor in Base..." << endl;}
    Base(const Base &){cout << "copy ctor in Base..." << endl;}
};

class Derived : public Base {
public:
    Derived(){cout  << "ctor in Derived..." << endl;}
    Derived(const Derived &b):Base(b) {cout << "copy ctor in Derived..." << endl;}
};

void f1()
{
    try{
        Derived d;  // ctor in Base , ctor in Derived
        Base &b = d;
        cout << "throw..." << endl;
        //  throw  Derived  
        throw d;  //copy ctor in Base  , copy ctor in Derived,          !!!
    }catch(Base &w) //Base&         Derived   ,     Base  Base&,     Derived     。
      //         Derived,     throw  throw  b,         。                   。
    {
        cout << "throw current exception 1, using throw only..." << endl;
        throw ; //      throw,     catch    Derived       Base  ,           Derived  。
    }
}

void test1()
{
    try{
        f1();
        cout << "never be here..." << endl;
    }catch(Derived)   //copy ctor in Base, copy ctor in Derived
    //}catch(Base) // copy  ctor in Base .
    {
        cout << "catch..." << endl;
    }
}

void f2()
{
    try{
        Derived d;  // print : ctor in Base  ctor in Derived
        Base &b = d;
        cout << "throw in f2..." << endl;
        throw b;  //copy ctor in Base //   throw         。
    }catch(Base &w)
    {
        cout << "throw current exception 2 " << endl;
        throw w; //copy ctor in Base
    }
}

void test2()
{
    try{
        f2();
        cout << "never be here in test2..." << endl;
    }catch(Base &) //      Base,  print: copy ctor in Base,   Base reference,         
    {
        cout << "catch in test2..." << endl;
    }
}
int main()
{
    cout << "1 ; "<< endl;
    test1();
    cout << "2 : " << endl;
    test2();
    return 0;
}

異なる点:投げ出されたオブジェクト(上記のように明らかに一時的なオブジェクト)はby referenceでキャプチャでき、by reference to constでキャプチャする必要はありませんが、関数呼び出しの過程でnon-const-referenceパラメータに一時的なオブジェクトを渡すことはできません.ただしexceptionには合法です.[non-const-referenceパラメータに一時オブジェクトを渡すことはなぜ許されないのか.]次の例を考える:
void f(const string &s)
{
    cout << s << endl;
    cout << "const-reference..." << endl;
    return;
}

void f2(string &s)
{
    cout << "non-const reference..." << endl;
    return;
}

int main(void)
{
    char buff[20] = "1234";
    //f f2        string   ,       buff    ,               ,       string  。
    f(buff);   //    。   const,       。
    f2(buff); //    ,  :f2    string non-const reference,     f2          s,      
    //f2    string     ,      ,       ,             s ,            ,      
    //non-const reference f2,        buff     。
    return 0;
}

異なる点:by valueで関数引数を渡すと、対応する関数パラメータに格納される被伝達オブジェクトのコピーになります.by valueでexceptionを渡すとどうなりますか?例:catch(Baseb).//予想される「投げ出されたもの」の「2つのコピー」の構造的代価を払わなければならない.1つは「いかなるexceptionでも生成される一時オブジェクトの「体に、もう1つは「一時オブジェクトをbにコピーする」ためである.by referenceであれば、関数には追加の動作は必要ありませんが、exceptionには「投げ出されたもの」のコピーの構造の代価があります.
by pointerでexceptionを渡す場合は、ローカルオブジェクトへのポインタを決して飛ばさないように注意しなければなりません.これも「義務コピー操作」が考慮しなければならない場合です.
異なる点:throw句とcatchパラメータのマッチング!!Exceptionとcatch句のマッチングプロセスは、アーキテクチャ内のクラス変換を継承する2つの変換にすぎません.(Base class exception用に作成されたcatch句で、derived classのexceptionをキャプチャできます).この変換規則はby value、by reference、またはby pointerの3つの方法に適用されます.2、1つの「有型ポインタ」から1つの「無型ポインタ」に変換されます.たとえば、const void*が設計したcatch句の場合、任意のポインタタイプのexceptionをキャプチャできます.
異なる点:catch句は常に表示される順序で一致します.すなわち,Derived class exceptionに対するcatch句は必ずBase class exceptionに対するcatch句の前に置く.
Catch exception By reference
理由:+by pointerであれば、その役割ドメインを超えても破棄されないオブジェクトが必要です.globalとstaticは手伝うことができますが、プログラマーたちはこれらを忘れるかもしれません.もしthrowの時に一時的にexceptionオブジェクト、例えばthrow new exceptionを構築した場合、catch句はこのオブジェクトを削除しますか?確かにglobalまたはstaticであれば、削除する必要はありませんが、newであれば削除する必要があります.+by valueの場合、オブジェクトカットの問題が発生し、Derived exceptionのオブジェクトをBase exceptionのオブジェクトに渡すと、オブジェクトカットの問題が発生します.この場合、catchブロックで虚関数を呼び出すと、その関数のバージョンはベースクラスのバージョンになります.明らかにこれは私たちが望んでいるものではありません.Catch exception by referenceを使用すると、これらの問題を回避できます.