例外安全入門
   @hotwatermorning
自己紹介

●
    @hotwatermorning
●
    はてなid:heisesswasser
●
    大学生
●
    DTMやってます
●
    C++が好きです
●
    「プログラミングの魔導少女」では
    「RangeとPStade.Oven」という記事を書かせ
    ていただきました。
C++erなら
誰しもメモリーリークに
悩まされたことがあるはず
今回のセッションは
その悩みを
軽減する
軽減する
  かもしれない
本日のレシピ

1.例外安全とは?
2.例外安全性の種類
3.例外安全なコードを書くには
4.例外安全にするための技法
本日のレシピ

1.例外安全とは?
2.例外安全性の種類
3.例外安全なコードを書くには
4.例外安全にするための技法

いわゆる、エラーハンドリング/例外ハンドリング関連の内容
●

は含んでおりません。
本日のレシピ

1.例外安全とは?
2.例外安全性の種類
3.例外安全なコードを書くには
4.例外安全にするための技法
● いわゆる、エラーハンドリング/例外ハンドリング関連の内容
は含んでおりません。
●
  というかそれは僕も知りたいので誰か教えt(ry
1.例外安全とは?
例外安全とは?

「あるコード内を実行中の失敗が、メモリリー
ク、格納データの不整合、不正な出力などの有
害な効果を生じないとき、そのコード片は例外
安全であると言う。」
“例外処理”
http://ja.wikipedia.org/wiki/%E4%BE%8B
%E5%A4%96%E5%87%A6%E7%90%86
例外安全とは?

「例外安全なプログラミングとは、 例外を投げ
る可能性があるコードが実際に例外を投げた場
合に、 プログラムの状態が壊れずリソースも
リークしないように作るプログラミングのこと
を言います。 」
“例外安全なプログラミング”
http://www.kmonos.net/alang/d/2.0/exception-safe.html
例外安全とは?

●
    例外が発生しても適切に対処できるよう
    なコードを書く。
●
    適切に対処しないと・・・
     ‣   メモリリーク、リソースリーク
     ‣   中途半端な操作の完了
     ‣   デストラクタから例外が投げられる
          と・・・
例外安全でないコードの例(1)

void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];
    read_data(data_, n);
}
例外安全でないコードの例(1)

void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];    //←ここや
    read_data(data_, n);
}
例外安全でないコードの例(1)

void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];   //←ここや
    read_data(data_, n);//←ここで
}
例外安全でないコードの例(1)

void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];   //←ここや
    read_data(data_, n);//←ここで
}        //例外が投げられると...??
例外安全でないコードの例(1)

void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];   //←ここや
    read_data(data_, n);//←ここで
}        //例外が投げられると...??
SampleClassの状態が壊れてしまう!!
例外安全でないコードの例(2)

SampleClass2 &
    SampleClass2::operator=
        (SampleClass2 const &rhs)
{
    data1_ = rhs.data1_;
    data2_ = rhs.data2_;
    data3_ = rhs.data3_;
    data4_ = rhs.data4_;
    return *this;
}
例外安全でないコードの例(2)

SampleClass2 &
    SampleClass2::operator=
        (SampleClass2 const &rhs)
{
    data1_ = rhs.data1_;
    data2_ = rhs.data2_;//このうち
    data3_ = rhs.data3_;
    data4_ = rhs.data4_;
    return *this;
}
例外安全でないコードの例(2)

SampleClass2 &
    SampleClass2::operator=
        (SampleClass2 const &rhs)
{
    data1_ = rhs.data1_;
    data2_ = rhs.data2_;//このうち
    data3_ = rhs.data3_;//どれかで
    data4_ = rhs.data4_;
    return *this;
}
例外安全でないコードの例(2)

SampleClass2 &
    SampleClass2::operator=
        (SampleClass2 const &rhs)
{
    data1_ = rhs.data1_;
    data2_ = rhs.data2_;//このうち
    data3_ = rhs.data3_;//どれかで
    data4_ = rhs.data4_;//例外が起きたら
    return *this;
}
例外安全でないコードの例(2)

SampleClass2 &
    SampleClass2::operator=
        (SampleClass2 const &rhs)
{
    data1_ = rhs.data1_;//これはどうなる?
    data2_ = rhs.data2_;//このうち
    data3_ = rhs.data3_;//どれかで
    data4_ = rhs.data4_;//例外が起きたら
    return *this;
}
例外安全なコードじゃないと
例外が起きたときに
きちんと対処できない
よし、
例外安全なコードを書こう!
と、そのまえに
2.例外安全性の種類
例外安全性の種類

●
  例外を投げない保証     高
●
  強い例外安全
              例外安全性
●
  基本的な例外安全
●
  例外安全保証なし      低
例外安全性の種類

●
  例外を投げない保証
●
  強い例外安全
              但し・・・
●
  基本的な例外安全
●
  例外安全保証なし
例外安全性の種類

●
  例外を投げない保証     高
●
  強い例外安全
               コスト…
●
  基本的な例外安全
●
  例外安全保証なし      低
例外安全性の種類

●
    例外安全保証なし
例外安全性の種類

       ●
           例外安全保証なし
●
    その関数や呼び出し先で例外が起きると
    リソースがリークしてしまうかもしれな
    い・・・
例外安全性の種類

       ●
           例外安全保証なし
void SampleClass::ReadData(int n)
{
    delete [] data_;
    data_ = new T[n];
    read_data(data_, n);
}
例外安全性の種類

●
    基本的な例外安全の保証
例外安全性の種類

     ●
         基本的な例外安全の保証
●
    例外が投げられても、いかなるリソース
    もリークしない。
例外安全性の種類

     ●
         基本的な例外安全の保証
●
    例外が投げられても、いかなるリソース
    もリークしない。
●
    副作用は出るかもしれない(データの変
    更や出力など。)
例外安全性の種類

     ●
         基本的な例外安全の保証
●
    例外が投げられても、いかなるリソース
    もリークしない。
●
    コンテナの中で例外が発生した場合、一
    貫性は保っているが、予測可能な状態と
    は限らない。
●
    なのでその後、削除や再利用はできる。
例外安全性の種類

       ●
           基本的な例外安全の保証
void SampleClass::ReadData(int n)
{
    delete [] data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
    } catch(...) {
        delete [] data_;    data_ = 0;
    }
}
例外安全性の種類

●
    強い例外安全の保証
例外安全性の種類

      ●
          強い例外安全の保証
●
    例外が起きたなら、全ての変更は
    ロールバックされる
●
    完全な成功か、例外による完全な失
    敗かの2つの状況になる
例外安全性の種類

           ●
               強い例外安全の保証
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全性の種類

●
    例外を投げない保証(no throw)
例外安全性の種類

    ●
        例外を投げない保証(no throw)
●
    操作は全て正しく完了されることが
    保証される。
例外安全性の種類

    ●
        例外を投げない保証(no throw)
●
    操作は全て正しく完了されることが
    保証される。
●
    例)基本データ型の代入などは例外
    が起きない
例外安全性の種類

    ●
        例外を投げない保証(no throw)
●
    操作は全て正しく完了されることが
    保証される。
●
    例)基本データ型の代入などは例外
    が起きない
●
    呼び出し先も例外を投げない保証が
    できなければならない
例外安全性の種類

●
    例外を投げない保証(no throw)
void foo()
{
    int n1 =   0x10;
    int n2 =   n1;
    int *pn1   = &n1;
    int *pn2   = pn1;
}
3.例外安全なコードを書くには
例外安全なコードを書くには

●
    ある実行パス中の例外安全性は、その工
    程で一番低いものになる。
例外安全なコードを書くには

●
    ある実行パス中の例外安全性は、その工
    程で一番低いものになる。
●
    どゆこと?
例外安全なコードを書くには

int baz() { return bar() * bar(); }
void foo()
{
    int n1 = 1;     //例外安全なコード
    int n2 = bar(); //例外安全でないコード
    int n3 = 3;     //例外安全なコード
    int n4 = baz(); //例外安全でないコード
}
例外安全なコードを書くには
(さっきの「強い例外安全」のサンプルコード)

void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全なコードを書くには
(さっきの「強い例外安全」のサンプルコード)

void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n); //←もしこの関数が
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全なコードを書くには
(さっきの「強い例外安全」のサンプルコード)

void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n); //←もしこの関数が
        delete [] tmp;       //例外安全じゃないと
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全なコードを書くには
(さっきの「強い例外安全」のサンプルコード)

void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n); //←もしこの関数が
        delete [] tmp;       //例外安全じゃないと
    } catch(...) {           //ReadData関数は
        delete [] data_; d...//完全な例外安全とは
    }                        //言えなくなる
}
例外安全なコードを書くには

●
    ある実行パス中の例外安全性は、その工
    程で一番低いものになる。
●
    呼び出し先の例外安全性にも左右されて
    しまう。
例外安全なコードを書くには

●
    ある実行パス中の例外安全性は、その工
    程で一番低いものになる。
●
    呼び出し先の例外安全性にも左右されて
    しまう。
●
    プログラムの中で基本的な機能であるほ
    ど、しっかりした例外安全性を実装して
    いなければならない。(コンテナなど)
例外安全なコードを書くには

●
    ある実行パス中の例外安全性は、その工
    程で一番低いものになる。
●
    呼び出し先の例外安全性にも左右されて
    しまう。
●
    プログラムの中で基本的な機能であるほ
    ど、しっかりした例外安全性を実装して
    いなければならない。(コンテナなど)
●
    標準のコンテナは大体強い保証を満たす
例外安全なコードを書くには

●
    それぞれの関数で例外を投げうる部分と
    投げない部分を明確に分離する。
●
    本来の処理の成功を確認した時点で、例
    外を投げない部分を使って、状態の変更
    と後処理を行うようにする。
例外安全なコードを書くには

●
    例外中立について
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }   //例外安全に書けましたね!
}
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }   //でもいざ例外が起きたときに
}
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }   //ReadDataの呼び出し元は
}       //ここで起きた例外をなにも知らない
例外安全なコードを書くには

●
    例外中立について
●
    コンテナなどで例外が起きると、その例
    外が起きるまでのコンテキストを知って
    いるのは呼び出し元だけ。
例外安全なコードを書くには

●
    例外中立について
●
    コンテナなどで例外が起きると、その例
    外が起きるまでのコンテキストを知って
    いるのは呼び出し元だけ。
●
    コンテナでは、自分の行った範囲の例外
    は安全に処理できるけど、他は無理。
例外安全なコードを書くには

●
    例外中立について
●
    コンテナなどで例外が起きると、その例
    外が起きるまでのコンテキストを知って
    いるのは呼び出し元だけ。
●
    コンテナでは、自分の行った範囲の例外
    は安全に処理できるけど、他は無理。
●
    適切に処理できるところまでは例外を正
    しく伝える。
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全なコードを書くには
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;     //正しくはこう。
    }
}
例外安全なコードを書くには

●
    単一の関数にatomicでない複数の処理が
    あると、例外安全の強い保証をするのは
    不可能。
●
    例)std::coutに出力してからstd::cerr
    に出力
4.例外安全にするための技法
これまでのことは
いまから紹介する
2つのテクニックの
布石に過ぎなかった
布石に過ぎなかった
      \ばばーん/
例外安全にするための技法

●
    いままで説明してきたことを上手く実装
    するためのテクニック
例外安全にするための技法

●
    いままで説明してきたことを上手く実装
    するためのテクニック
●
    Swap
例外安全にするための技法

●
    いままで説明してきたことを上手く実装
    するためのテクニック
●
    Swap
●
    RAII(Resource Acquisition Is
    Initialization)
例外安全にするための技法

           ●
               Swap
●
    実体を他に作って、全て成功した
    ら、変更したいリソースとSwapする
例外安全にするための技法

           ●
               Swap
●
    実体を他に作って、全て成功した
    ら、変更したいリソースとSwapする
●
    Swapは、例外を投げない保証を必ず
    守る
例外安全にするための技法
template<typename T>
void swap(T &lhs, T &rhs)
{
    T tmp = lhs;
    lhs = rhs;
    rhs = tmp;
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //頑張って強い例外安全にしてるけど
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //ロールバック用にいろいろ書いてて
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //例外を投げる部分が
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //例外を投げない部分に囲まれて
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //Try-Catchも入り乱れて
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //ちょっと複雑・・・
}
例外安全なコードを書くには

●
    それぞれの関数で例外を投げうる部分と
    投げない部分を明確に分離する。
●
    本来の処理の成功を確認した時点で、例
    外を投げない部分を使って、状態の変更
    と後処理を行うようにする。
例外安全にするための技法

           ●
               Swap
●
    実体を他に作って、全て成功した
    ら、変更したいリソースとSwap
例外安全にするための技法

            ●
                Swap
●
    実体を他に作って、全て成功した
    ら、変更したいリソースとSwap


例外を投げうる部分
                       投げない部分
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = new T[n];     //先に確保
    try {
        read_data(tmp, n);//そいつで処理
    } catch(...) {
        delete [] tmp;
    }

    std::swap(data_, tmp);//終わったら入れ替え
    delete [] tmp;        //後処理
}
例外安全にするための技法

             ●
                 Swap
●
    例外を投げうる部分と投げない部分を明
    確に分離
●
    例外を投げる部分が成功したら例外を投
    げない部分で状態を変更
●
    ロールバック用のTry-Catchは要らなく
    なる!
例外安全にするための技法

             ●
                 Swap
●
    例外を投げうる部分と投げない部分を明
    確に分離
●
    例外を投げる部分が成功したら例外を投
    げない部分で状態を変更
●
    ロールバック用のTry-Catchは要らなく
    なる!
    (リソース管理用のは必要・・・)
例外安全にするための技法

              ●
                  Swap
●
    自作クラスでも、
    Copy-Constructableなクラスなら
    swapを実装する。
例外安全にするための技法
void SampleClass::swap(SampleClass &rhs)
{
    //標準のswap関数や
    std::swap(data_, rhs.data_);
    //それ用のswap関数などで
    other_data_.swap(rhs.other_data_);
}
例外安全にするための技法

                  ●
                      Swap
●
    自作クラスでも、
    Copy-Constructableなクラスなら
    swapを実装する。
●
    SampleClass
        (SampleClass const &rhs);
例外安全にするための技法

              ●
                  Swap
●
    自作クラスでも、
    Copy-Constructableなクラスなら
    swapを実装する。
●
    実装すると・・・
例外安全にするための技法

                    ●
                        Swap
●
    自作クラスでも、
    Copy-Constructableなクラスなら
    swapを実装する。
●
    Assignableなクラスに出来る。
●
    SampleClass &
      operator=(SampleClass const &)
例外安全にするための技法
SampleClass &
    SampleClass::operator=
        (SampleClass const &rhs)
{
    //コピーコンストラクタ
    SampleClass tmp(rhs);
    //swap
    tmp.swap(*this);
    return *this;
}
例外安全にするための技法
SampleClass &
    SampleClass::operator=
        (SampleClass const &rhs)
{
    //コピーコンストラクタ
    SampleClass tmp(rhs);
    //swap
    tmp.swap(*this);      さらにまとめて
    return *this;         書くと
}
例外安全にするための技法
SampleClass &
    SampleClass::operator=
        (SampleClass const &rhs)
{
    SampleClass(rhs).swap(*this);
    return *this;
}
例外安全にするための技法
SampleClass &
    SampleClass::operator=
        (SampleClass const &rhs)
{
    SampleClass(rhs).swap(*this);
    return *this;
}
 メンバ変数がAssignableであることを
 要求しない
例外安全にするための技法
SampleClass &
    SampleClass::operator=
        (SampleClass const &rhs)
{
    SampleClass(rhs).swap(*this);
    return *this;
}
             Copy And Swap
例外安全にするための技法

          ●
              RAII
●
    「リソースの確保は初期化である」
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //リソース管理のためにコードが複雑に
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //deleteも分散してしまった
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    } //対応していない
}
例外安全にするための技法

          ●
              RAII
●
    「リソースの確保は初期化である」
●
    コンストラクタでリソースを確保
●
    デストラクタでリソースを破棄
例外安全にするための技法

          ●
              RAII
●
    「リソースの確保は初期化である」
●
    コンストラクタでリソースを確保
●
    デストラクタでリソースを破棄

        対応!!
例外安全にするための技法

                    ●
                        RAII
class RAIIClass
{
    RAIIClass(Resource r) : r_(r) {}
    RAIIClass(Args as) : r_(CreateResource(as))
    {}

    ~RAIIClass() { DisposeResource(r_); }
private:
    Resource r_;
};
例外安全にするための技法

          ●
              RAII
●
    これをメモリ管理に使ったのがいわ
    ゆるスマートポインタ
例外安全にするための技法

          ●
              RAII
●
    これをメモリ管理に使ったのがいわ
    ゆるスマートポインタ
●
    プログラマはリソース解放のめんど
    くささから解放される!
例外安全にするための技法

          ●
              RAII
●
    これをメモリ管理に使ったのがいわ
    ゆるスマートポインタ
●
    プログラマはリソース解放のめんど
    くささから解放される!
●
    例外安全とか考えなくても使うべ
    き!
例外安全にするための技法

          ●
              RAII
●
    これがどのように例外安全性の点で
    重宝されるのか?
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    }
}
例外安全にするための技法

            ●
                RAII
●
    Deleteのために例外をCatchしなく
    ちゃいけなかった
例外安全にするための技法

            ●
                RAII
●
    Deleteのために例外をCatchしなく
    ちゃいけなかった
●
    でもRAIIなクラスを使うと、Delete
    は自動化できる
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];      これが
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
        throw;
    }
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    shared_array<T> tmp = data_;
    try {
        data_.reset(new T[n]); こうなる
        read_data(data_, n);
    } catch (...) {
        data_ = tmp;
    }
}
例外安全にするための技法

            ●
                RAII
●
    どこで起きるかわからないような例
    外によって関数を抜けるときも、そ
    の時に自動でリソースが解放され
    る!
●
    リソース解放のためのTry-Catchは要
    らなくなる!
例外安全にするための技法

            ●
                RAII
●
    どこで起きるかわからないような例
    外によって関数を抜けるときも、そ
    の時に自動でリソースが解放され
    る!
●
    リソース解放のためのTry-Catchは要
    らなくなる!(強い例外安全保証の
    ためのは必要・・・)
例外安全にするための技法

   ●
       SwapとRAII
例外安全にするための技法

      ●
          SwapとRAII
1.Swapはロールバック用のTry-
 Catchを不要にする。
例外安全にするための技法

      ●
          SwapとRAII
1.Swapはロールバック用のTry-
 Catchを不要にする。
2.RAIIはリソース管理用のTry-
 Catchを不要にする。
例外安全にするための技法

      ●
          SwapとRAII
1.Swapはロールバック用のTry-
 Catchを不要にする。
2.RAIIはリソース管理用のTry-
 Catchを不要にする。
3.例外中立のために、例外が起き
 てもいじらないで伝える。
例外安全にするための技法

   ●
       SwapとRAII



 合体
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    shared_array<T> tmp(new T[n]);
    read_data(tmp_, n);
    std::swap(data_, tmp);
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    shared_array<T> tmp(new T[n]);
    read_data(tmp_, n);
    std::swap(data_, tmp);
}

      おわかりいただけただろうか
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    shared_array<T> tmp(new T[n]);
    read_data(tmp_, n);
    std::swap(data_, tmp);
}

      おわかりいただけただろうか
      ではもう一度・・・
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    T *tmp = data_; data_ = 0;
    try {
        data_ = new T[n];
        read_data(data_, n);
        delete [] tmp;
    } catch(...) {
        delete [] data_; data_ = tmp;
    }
}
例外安全にするための技法
void SampleClass::ReadData(int n)
{
    shared_array<T> tmp(new T[n]);
    read_data(tmp_, n);
    std::swap(data_, tmp);
}
例外安全にするための技法

         ●
             SwapとRAII
●
    適切なコードを書けば、例外安全
    性のためにTry-Catchを書く必要
    はない。
例外安全にするための技法

 ●
     デストラクタで例外
例外安全にするための技法

      ●
          デストラクタで例外
●
    禁止!
例外安全にするための技法

      ●
          デストラクタで例外
●
    禁止!
●
    配列を安全にできない
●
    例外が起きたときにスタックの巻
    き戻しを安全にできない
●
    全ての例外安全の努力が水の泡!
5.まとめ
まとめ

●
    例外安全なコードを書くには
まとめ

●
    例外安全なコードを書くには
●
    例外を投げる部分と投げない部分を
    分離して、例外のある処理の成功を
    確認してから、状態を変更する。
●
    RAIIを使用してリソースの管理を自
    動化する
まとめ

●
    今回の内容はほとんど全て
    「Exceptional C++」
    (ハーブ・サッター著)
    に書かれています
●
    あと「C++ Coding Standard」も
おしまい。

Sapporocpp#2 exception-primer