Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
例外安全入門   @hotwatermorning
自己紹介●    @hotwatermorning●    はてなid:heisesswasser●    大学生●    DTMやってます●    C++が好きです●    「プログラミングの魔導少女」では    「RangeとPStade....
C++erなら
誰しもメモリーリークに
悩まされたことがあるはず
今回のセッションは
その悩みを
軽減する
軽減する  かもしれない
本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法
本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法いわゆる、エラーハンドリング/例外ハンドリング関連の内容●は含んでおりません。
本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法● いわゆる、エラーハンドリング/例外ハンドリング関連の内容は含んでおりません。●  というかそれは僕も知りたいので誰か教えt(ry
1.例外安全とは?
例外安全とは?「あるコード内を実行中の失敗が、メモリリーク、格納データの不整合、不正な出力などの有害な効果を生じないとき、そのコード片は例外安全であると言う。」“例外処理”http://ja.wikipedia.org/wiki/%E4%BE%...
例外安全とは?「例外安全なプログラミングとは、 例外を投げる可能性があるコードが実際に例外を投げた場合に、 プログラムの状態が壊れずリソースもリークしないように作るプログラミングのことを言います。 」“例外安全なプログラミング”http://w...
例外安全とは?●    例外が発生しても適切に対処できるよう    なコードを書く。●    適切に対処しないと・・・     ‣   メモリリーク、リソースリーク     ‣   中途半端な操作の完了     ‣   デストラクタから例外が投...
例外安全でないコードの例(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)...
例外安全でないコードの例(2)SampleClass2 &    SampleClass2::operator=        (SampleClass2 const &rhs){    data1_ = rhs.data1_;    data...
例外安全でないコードの例(2)SampleClass2 &    SampleClass2::operator=        (SampleClass2 const &rhs){    data1_ = rhs.data1_;    data...
例外安全でないコードの例(2)SampleClass2 &    SampleClass2::operator=        (SampleClass2 const &rhs){    data1_ = rhs.data1_;    data...
例外安全でないコードの例(2)SampleClass2 &    SampleClass2::operator=        (SampleClass2 const &rhs){    data1_ = rhs.data1_;    data...
例外安全でないコードの例(2)SampleClass2 &    SampleClass2::operator=        (SampleClass2 const &rhs){    data1_ = rhs.data1_;//これはどうな...
例外安全なコードじゃないと
例外が起きたときにきちんと対処できない
よし、
例外安全なコードを書こう!
と、そのまえに
2.例外安全性の種類
例外安全性の種類●  例外を投げない保証     高●  強い例外安全              例外安全性●  基本的な例外安全●  例外安全保証なし      低
例外安全性の種類●  例外を投げない保証●  強い例外安全              但し・・・●  基本的な例外安全●  例外安全保証なし
例外安全性の種類●  例外を投げない保証     高●  強い例外安全               コスト…●  基本的な例外安全●  例外安全保証なし      低
例外安全性の種類●    例外安全保証なし
例外安全性の種類       ●           例外安全保証なし●    その関数や呼び出し先で例外が起きると    リソースがリークしてしまうかもしれな    い・・・
例外安全性の種類       ●           例外安全保証なしvoid SampleClass::ReadData(int n){    delete [] data_;    data_ = new T[n];    read_dat...
例外安全性の種類●    基本的な例外安全の保証
例外安全性の種類     ●         基本的な例外安全の保証●    例外が投げられても、いかなるリソース    もリークしない。
例外安全性の種類     ●         基本的な例外安全の保証●    例外が投げられても、いかなるリソース    もリークしない。●    副作用は出るかもしれない(データの変    更や出力など。)
例外安全性の種類     ●         基本的な例外安全の保証●    例外が投げられても、いかなるリソース    もリークしない。●    コンテナの中で例外が発生した場合、一    貫性は保っているが、予測可能な状態と    は限らな...
例外安全性の種類       ●           基本的な例外安全の保証void SampleClass::ReadData(int n){    delete [] data_; data_ = 0;    try {        da...
例外安全性の種類●    強い例外安全の保証
例外安全性の種類      ●          強い例外安全の保証●    例外が起きたなら、全ての変更は    ロールバックされる●    完全な成功か、例外による完全な失    敗かの2つの状況になる
例外安全性の種類           ●               強い例外安全の保証void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {     ...
例外安全性の種類●    例外を投げない保証(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(); //例外安全でないコード ...
例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ ...
例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ ...
例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ ...
例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ ...
例外安全なコードを書くには●    ある実行パス中の例外安全性は、その工    程で一番低いものになる。●    呼び出し先の例外安全性にも左右されて    しまう。
例外安全なコードを書くには●    ある実行パス中の例外安全性は、その工    程で一番低いものになる。●    呼び出し先の例外安全性にも左右されて    しまう。●    プログラムの中で基本的な機能であるほ    ど、しっかりした例外安全...
例外安全なコードを書くには●    ある実行パス中の例外安全性は、その工    程で一番低いものになる。●    呼び出し先の例外安全性にも左右されて    しまう。●    プログラムの中で基本的な機能であるほ    ど、しっかりした例外安全...
例外安全なコードを書くには●    それぞれの関数で例外を投げうる部分と    投げない部分を明確に分離する。●    本来の処理の成功を確認した時点で、例    外を投げない部分を使って、状態の変更    と後処理を行うようにする。
例外安全なコードを書くには●    例外中立について
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くには●    例外中立について●    コンテナなどで例外が起きると、その例    外が起きるまでのコンテキストを知って    いるのは呼び出し元だけ。
例外安全なコードを書くには●    例外中立について●    コンテナなどで例外が起きると、その例    外が起きるまでのコンテキストを知って    いるのは呼び出し元だけ。●    コンテナでは、自分の行った範囲の例外    は安全に処理でき...
例外安全なコードを書くには●    例外中立について●    コンテナなどで例外が起きると、その例    外が起きるまでのコンテキストを知って    いるのは呼び出し元だけ。●    コンテナでは、自分の行った範囲の例外    は安全に処理でき...
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くにはvoid SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        rea...
例外安全なコードを書くには●    単一の関数に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...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全なコードを書くには●    それぞれの関数で例外を投げうる部分と    投げない部分を明確に分離する。●    本来の処理の成功を確認した時点で、例    外を投げない部分を使って、状態の変更    と後処理を行うようにする。
例外安全にするための技法           ●               Swap●    実体を他に作って、全て成功した    ら、変更したいリソースとSwap
例外安全にするための技法            ●                Swap●    実体を他に作って、全て成功した    ら、変更したいリソースとSwap例外を投げうる部分                       投げない部分
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = new T[n];     //先に確保    try {        read_data(tmp, n);//そいつで処理...
例外安全にするための技法             ●                 Swap●    例外を投げうる部分と投げない部分を明    確に分離●    例外を投げる部分が成功したら例外を投    げない部分で状態を変更●    ロ...
例外安全にするための技法             ●                 Swap●    例外を投げうる部分と投げない部分を明    確に分離●    例外を投げる部分が成功したら例外を投    げない部分で状態を変更●    ロ...
例外安全にするための技法              ●                  Swap●    自作クラスでも、    Copy-Constructableなクラスなら    swapを実装する。
例外安全にするための技法void SampleClass::swap(SampleClass &rhs){    //標準のswap関数や    std::swap(data_, rhs.data_);    //それ用のswap関数などで  ...
例外安全にするための技法                  ●                      Swap●    自作クラスでも、    Copy-Constructableなクラスなら    swapを実装する。●    Sampl...
例外安全にするための技法              ●                  Swap●    自作クラスでも、    Copy-Constructableなクラスなら    swapを実装する。●    実装すると・・・
例外安全にするための技法                    ●                        Swap●    自作クラスでも、    Copy-Constructableなクラスなら    swapを実装する。●    A...
例外安全にするための技法SampleClass &    SampleClass::operator=        (SampleClass const &rhs){    //コピーコンストラクタ    SampleClass tmp(rh...
例外安全にするための技法SampleClass &    SampleClass::operator=        (SampleClass const &rhs){    //コピーコンストラクタ    SampleClass tmp(rh...
例外安全にするための技法SampleClass &    SampleClass::operator=        (SampleClass const &rhs){    SampleClass(rhs).swap(*this);    r...
例外安全にするための技法SampleClass &    SampleClass::operator=        (SampleClass const &rhs){    SampleClass(rhs).swap(*this);    r...
例外安全にするための技法SampleClass &    SampleClass::operator=        (SampleClass const &rhs){    SampleClass(rhs).swap(*this);    r...
例外安全にするための技法          ●              RAII●    「リソースの確保は初期化である」
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法          ●              RAII●    「リソースの確保は初期化である」●    コンストラクタでリソースを確保●    デストラクタでリソースを破棄
例外安全にするための技法          ●              RAII●    「リソースの確保は初期化である」●    コンストラクタでリソースを確保●    デストラクタでリソースを破棄        対応!!
例外安全にするための技法                    ●                        RAIIclass RAIIClass{    RAIIClass(Resource r) : r_(r) {}    RAIIC...
例外安全にするための技法          ●              RAII●    これをメモリ管理に使ったのがいわ    ゆるスマートポインタ
例外安全にするための技法          ●              RAII●    これをメモリ管理に使ったのがいわ    ゆるスマートポインタ●    プログラマはリソース解放のめんど    くささから解放される!
例外安全にするための技法          ●              RAII●    これをメモリ管理に使ったのがいわ    ゆるスマートポインタ●    プログラマはリソース解放のめんど    くささから解放される!●    例外安全と...
例外安全にするための技法          ●              RAII●    これがどのように例外安全性の点で    重宝されるのか?
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法            ●                RAII●    Deleteのために例外をCatchしなく    ちゃいけなかった
例外安全にするための技法            ●                RAII●    Deleteのために例外をCatchしなく    ちゃいけなかった●    でもRAIIなクラスを使うと、Delete    は自動化できる
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];      これが   ...
例外安全にするための技法void SampleClass::ReadData(int n){    shared_array<T> tmp = data_;    try {        data_.reset(new T[n]); こうなる...
例外安全にするための技法            ●                RAII●    どこで起きるかわからないような例    外によって関数を抜けるときも、そ    の時に自動でリソースが解放され    る!●    リソース解放...
例外安全にするための技法            ●                RAII●    どこで起きるかわからないような例    外によって関数を抜けるときも、そ    の時に自動でリソースが解放され    る!●    リソース解放...
例外安全にするための技法   ●       SwapとRAII
例外安全にするための技法      ●          SwapとRAII1.Swapはロールバック用のTry- Catchを不要にする。
例外安全にするための技法      ●          SwapとRAII1.Swapはロールバック用のTry- Catchを不要にする。2.RAIIはリソース管理用のTry- Catchを不要にする。
例外安全にするための技法      ●          SwapとRAII1.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_...
例外安全にするための技法void SampleClass::ReadData(int n){    shared_array<T> tmp(new T[n]);    read_data(tmp_, n);    std::swap(data_...
例外安全にするための技法void SampleClass::ReadData(int n){    shared_array<T> tmp(new T[n]);    read_data(tmp_, n);    std::swap(data_...
例外安全にするための技法void SampleClass::ReadData(int n){    T *tmp = data_; data_ = 0;    try {        data_ = new T[n];        read...
例外安全にするための技法void SampleClass::ReadData(int n){    shared_array<T> tmp(new T[n]);    read_data(tmp_, n);    std::swap(data_...
例外安全にするための技法         ●             SwapとRAII●    適切なコードを書けば、例外安全    性のためにTry-Catchを書く必要    はない。
例外安全にするための技法 ●     デストラクタで例外
例外安全にするための技法      ●          デストラクタで例外●    禁止!
例外安全にするための技法      ●          デストラクタで例外●    禁止!●    配列を安全にできない●    例外が起きたときにスタックの巻    き戻しを安全にできない●    全ての例外安全の努力が水の泡!
5.まとめ
まとめ●    例外安全なコードを書くには
まとめ●    例外安全なコードを書くには●    例外を投げる部分と投げない部分を    分離して、例外のある処理の成功を    確認してから、状態を変更する。●    RAIIを使用してリソースの管理を自    動化する
まとめ●    今回の内容はほとんど全て    「Exceptional C++」    (ハーブ・サッター著)    に書かれています●    あと「C++ Coding Standard」も
おしまい。
Upcoming SlideShare
Loading in …5
×

Sapporocpp#2 exception-primer

2,352 views

Published on

sapporo.cpp #2

Published in: Technology, Business
  • Login to see the comments

Sapporocpp#2 exception-primer

  1. 1. 例外安全入門 @hotwatermorning
  2. 2. 自己紹介● @hotwatermorning● はてなid:heisesswasser● 大学生● DTMやってます● C++が好きです● 「プログラミングの魔導少女」では 「RangeとPStade.Oven」という記事を書かせ ていただきました。
  3. 3. C++erなら
  4. 4. 誰しもメモリーリークに
  5. 5. 悩まされたことがあるはず
  6. 6. 今回のセッションは
  7. 7. その悩みを
  8. 8. 軽減する
  9. 9. 軽減する かもしれない
  10. 10. 本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法
  11. 11. 本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法いわゆる、エラーハンドリング/例外ハンドリング関連の内容●は含んでおりません。
  12. 12. 本日のレシピ1.例外安全とは?2.例外安全性の種類3.例外安全なコードを書くには4.例外安全にするための技法● いわゆる、エラーハンドリング/例外ハンドリング関連の内容は含んでおりません。● というかそれは僕も知りたいので誰か教えt(ry
  13. 13. 1.例外安全とは?
  14. 14. 例外安全とは?「あるコード内を実行中の失敗が、メモリリーク、格納データの不整合、不正な出力などの有害な効果を生じないとき、そのコード片は例外安全であると言う。」“例外処理”http://ja.wikipedia.org/wiki/%E4%BE%8B%E5%A4%96%E5%87%A6%E7%90%86
  15. 15. 例外安全とは?「例外安全なプログラミングとは、 例外を投げる可能性があるコードが実際に例外を投げた場合に、 プログラムの状態が壊れずリソースもリークしないように作るプログラミングのことを言います。 」“例外安全なプログラミング”http://www.kmonos.net/alang/d/2.0/exception-safe.html
  16. 16. 例外安全とは?● 例外が発生しても適切に対処できるよう なコードを書く。● 適切に対処しないと・・・ ‣ メモリリーク、リソースリーク ‣ 中途半端な操作の完了 ‣ デストラクタから例外が投げられる と・・・
  17. 17. 例外安全でないコードの例(1)void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; read_data(data_, n);}
  18. 18. 例外安全でないコードの例(1)void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);}
  19. 19. 例外安全でないコードの例(1)void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);//←ここで}
  20. 20. 例外安全でないコードの例(1)void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);//←ここで} //例外が投げられると...??
  21. 21. 例外安全でないコードの例(1)void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);//←ここで} //例外が投げられると...??SampleClassの状態が壊れてしまう!!
  22. 22. 例外安全でないコードの例(2)SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_; data3_ = rhs.data3_; data4_ = rhs.data4_; return *this;}
  23. 23. 例外安全でないコードの例(2)SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_;//このうち data3_ = rhs.data3_; data4_ = rhs.data4_; return *this;}
  24. 24. 例外安全でないコードの例(2)SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_;//このうち data3_ = rhs.data3_;//どれかで data4_ = rhs.data4_; return *this;}
  25. 25. 例外安全でないコードの例(2)SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_;//このうち data3_ = rhs.data3_;//どれかで data4_ = rhs.data4_;//例外が起きたら return *this;}
  26. 26. 例外安全でないコードの例(2)SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_;//これはどうなる? data2_ = rhs.data2_;//このうち data3_ = rhs.data3_;//どれかで data4_ = rhs.data4_;//例外が起きたら return *this;}
  27. 27. 例外安全なコードじゃないと
  28. 28. 例外が起きたときにきちんと対処できない
  29. 29. よし、
  30. 30. 例外安全なコードを書こう!
  31. 31. と、そのまえに
  32. 32. 2.例外安全性の種類
  33. 33. 例外安全性の種類● 例外を投げない保証 高● 強い例外安全 例外安全性● 基本的な例外安全● 例外安全保証なし 低
  34. 34. 例外安全性の種類● 例外を投げない保証● 強い例外安全 但し・・・● 基本的な例外安全● 例外安全保証なし
  35. 35. 例外安全性の種類● 例外を投げない保証 高● 強い例外安全 コスト…● 基本的な例外安全● 例外安全保証なし 低
  36. 36. 例外安全性の種類● 例外安全保証なし
  37. 37. 例外安全性の種類 ● 例外安全保証なし● その関数や呼び出し先で例外が起きると リソースがリークしてしまうかもしれな い・・・
  38. 38. 例外安全性の種類 ● 例外安全保証なしvoid SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; read_data(data_, n);}
  39. 39. 例外安全性の種類● 基本的な例外安全の保証
  40. 40. 例外安全性の種類 ● 基本的な例外安全の保証● 例外が投げられても、いかなるリソース もリークしない。
  41. 41. 例外安全性の種類 ● 基本的な例外安全の保証● 例外が投げられても、いかなるリソース もリークしない。● 副作用は出るかもしれない(データの変 更や出力など。)
  42. 42. 例外安全性の種類 ● 基本的な例外安全の保証● 例外が投げられても、いかなるリソース もリークしない。● コンテナの中で例外が発生した場合、一 貫性は保っているが、予測可能な状態と は限らない。● なのでその後、削除や再利用はできる。
  43. 43. 例外安全性の種類 ● 基本的な例外安全の保証void SampleClass::ReadData(int n){ delete [] data_; data_ = 0; try { data_ = new T[n]; read_data(data_, n); } catch(...) { delete [] data_; data_ = 0; }}
  44. 44. 例外安全性の種類● 強い例外安全の保証
  45. 45. 例外安全性の種類 ● 強い例外安全の保証● 例外が起きたなら、全ての変更は ロールバックされる● 完全な成功か、例外による完全な失 敗かの2つの状況になる
  46. 46. 例外安全性の種類 ● 強い例外安全の保証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; }}
  47. 47. 例外安全性の種類● 例外を投げない保証(no throw)
  48. 48. 例外安全性の種類 ● 例外を投げない保証(no throw)● 操作は全て正しく完了されることが 保証される。
  49. 49. 例外安全性の種類 ● 例外を投げない保証(no throw)● 操作は全て正しく完了されることが 保証される。● 例)基本データ型の代入などは例外 が起きない
  50. 50. 例外安全性の種類 ● 例外を投げない保証(no throw)● 操作は全て正しく完了されることが 保証される。● 例)基本データ型の代入などは例外 が起きない● 呼び出し先も例外を投げない保証が できなければならない
  51. 51. 例外安全性の種類● 例外を投げない保証(no throw)void foo(){ int n1 = 0x10; int n2 = n1; int *pn1 = &n1; int *pn2 = pn1;}
  52. 52. 3.例外安全なコードを書くには
  53. 53. 例外安全なコードを書くには● ある実行パス中の例外安全性は、その工 程で一番低いものになる。
  54. 54. 例外安全なコードを書くには● ある実行パス中の例外安全性は、その工 程で一番低いものになる。● どゆこと?
  55. 55. 例外安全なコードを書くにはint baz() { return bar() * bar(); }void foo(){ int n1 = 1; //例外安全なコード int n2 = bar(); //例外安全でないコード int n3 = 3; //例外安全なコード int n4 = baz(); //例外安全でないコード}
  56. 56. 例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)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; }}
  57. 57. 例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)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; }}
  58. 58. 例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)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; }}
  59. 59. 例外安全なコードを書くには(さっきの「強い例外安全」のサンプルコード)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...//完全な例外安全とは } //言えなくなる}
  60. 60. 例外安全なコードを書くには● ある実行パス中の例外安全性は、その工 程で一番低いものになる。● 呼び出し先の例外安全性にも左右されて しまう。
  61. 61. 例外安全なコードを書くには● ある実行パス中の例外安全性は、その工 程で一番低いものになる。● 呼び出し先の例外安全性にも左右されて しまう。● プログラムの中で基本的な機能であるほ ど、しっかりした例外安全性を実装して いなければならない。(コンテナなど)
  62. 62. 例外安全なコードを書くには● ある実行パス中の例外安全性は、その工 程で一番低いものになる。● 呼び出し先の例外安全性にも左右されて しまう。● プログラムの中で基本的な機能であるほ ど、しっかりした例外安全性を実装して いなければならない。(コンテナなど)● 標準のコンテナは大体強い保証を満たす
  63. 63. 例外安全なコードを書くには● それぞれの関数で例外を投げうる部分と 投げない部分を明確に分離する。● 本来の処理の成功を確認した時点で、例 外を投げない部分を使って、状態の変更 と後処理を行うようにする。
  64. 64. 例外安全なコードを書くには● 例外中立について
  65. 65. 例外安全なコードを書くには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; }}
  66. 66. 例外安全なコードを書くには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; } //例外安全に書けましたね!}
  67. 67. 例外安全なコードを書くには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; } //でもいざ例外が起きたときに}
  68. 68. 例外安全なコードを書くには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の呼び出し元は} //ここで起きた例外をなにも知らない
  69. 69. 例外安全なコードを書くには● 例外中立について● コンテナなどで例外が起きると、その例 外が起きるまでのコンテキストを知って いるのは呼び出し元だけ。
  70. 70. 例外安全なコードを書くには● 例外中立について● コンテナなどで例外が起きると、その例 外が起きるまでのコンテキストを知って いるのは呼び出し元だけ。● コンテナでは、自分の行った範囲の例外 は安全に処理できるけど、他は無理。
  71. 71. 例外安全なコードを書くには● 例外中立について● コンテナなどで例外が起きると、その例 外が起きるまでのコンテキストを知って いるのは呼び出し元だけ。● コンテナでは、自分の行った範囲の例外 は安全に処理できるけど、他は無理。● 適切に処理できるところまでは例外を正 しく伝える。
  72. 72. 例外安全なコードを書くには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; }}
  73. 73. 例外安全なコードを書くには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; //正しくはこう。 }}
  74. 74. 例外安全なコードを書くには● 単一の関数にatomicでない複数の処理が あると、例外安全の強い保証をするのは 不可能。● 例)std::coutに出力してからstd::cerr に出力
  75. 75. 4.例外安全にするための技法
  76. 76. これまでのことは
  77. 77. いまから紹介する2つのテクニックの
  78. 78. 布石に過ぎなかった
  79. 79. 布石に過ぎなかった \ばばーん/
  80. 80. 例外安全にするための技法● いままで説明してきたことを上手く実装 するためのテクニック
  81. 81. 例外安全にするための技法● いままで説明してきたことを上手く実装 するためのテクニック● Swap
  82. 82. 例外安全にするための技法● いままで説明してきたことを上手く実装 するためのテクニック● Swap● RAII(Resource Acquisition Is Initialization)
  83. 83. 例外安全にするための技法 ● Swap● 実体を他に作って、全て成功した ら、変更したいリソースとSwapする
  84. 84. 例外安全にするための技法 ● Swap● 実体を他に作って、全て成功した ら、変更したいリソースとSwapする● Swapは、例外を投げない保証を必ず 守る
  85. 85. 例外安全にするための技法template<typename T>void swap(T &lhs, T &rhs){ T tmp = lhs; lhs = rhs; rhs = tmp;}
  86. 86. 例外安全にするための技法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; } //頑張って強い例外安全にしてるけど}
  87. 87. 例外安全にするための技法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; } //ロールバック用にいろいろ書いてて}
  88. 88. 例外安全にするための技法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; } //例外を投げる部分が}
  89. 89. 例外安全にするための技法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; } //例外を投げない部分に囲まれて}
  90. 90. 例外安全にするための技法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も入り乱れて}
  91. 91. 例外安全にするための技法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; } //ちょっと複雑・・・}
  92. 92. 例外安全なコードを書くには● それぞれの関数で例外を投げうる部分と 投げない部分を明確に分離する。● 本来の処理の成功を確認した時点で、例 外を投げない部分を使って、状態の変更 と後処理を行うようにする。
  93. 93. 例外安全にするための技法 ● Swap● 実体を他に作って、全て成功した ら、変更したいリソースとSwap
  94. 94. 例外安全にするための技法 ● Swap● 実体を他に作って、全て成功した ら、変更したいリソースとSwap例外を投げうる部分 投げない部分
  95. 95. 例外安全にするための技法void SampleClass::ReadData(int n){ T *tmp = new T[n]; //先に確保 try { read_data(tmp, n);//そいつで処理 } catch(...) { delete [] tmp; } std::swap(data_, tmp);//終わったら入れ替え delete [] tmp; //後処理}
  96. 96. 例外安全にするための技法 ● Swap● 例外を投げうる部分と投げない部分を明 確に分離● 例外を投げる部分が成功したら例外を投 げない部分で状態を変更● ロールバック用のTry-Catchは要らなく なる!
  97. 97. 例外安全にするための技法 ● Swap● 例外を投げうる部分と投げない部分を明 確に分離● 例外を投げる部分が成功したら例外を投 げない部分で状態を変更● ロールバック用のTry-Catchは要らなく なる! (リソース管理用のは必要・・・)
  98. 98. 例外安全にするための技法 ● Swap● 自作クラスでも、 Copy-Constructableなクラスなら swapを実装する。
  99. 99. 例外安全にするための技法void SampleClass::swap(SampleClass &rhs){ //標準のswap関数や std::swap(data_, rhs.data_); //それ用のswap関数などで other_data_.swap(rhs.other_data_);}
  100. 100. 例外安全にするための技法 ● Swap● 自作クラスでも、 Copy-Constructableなクラスなら swapを実装する。● SampleClass (SampleClass const &rhs);
  101. 101. 例外安全にするための技法 ● Swap● 自作クラスでも、 Copy-Constructableなクラスなら swapを実装する。● 実装すると・・・
  102. 102. 例外安全にするための技法 ● Swap● 自作クラスでも、 Copy-Constructableなクラスなら swapを実装する。● Assignableなクラスに出来る。● SampleClass & operator=(SampleClass const &)
  103. 103. 例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ //コピーコンストラクタ SampleClass tmp(rhs); //swap tmp.swap(*this); return *this;}
  104. 104. 例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ //コピーコンストラクタ SampleClass tmp(rhs); //swap tmp.swap(*this); さらにまとめて return *this; 書くと}
  105. 105. 例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;}
  106. 106. 例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;} メンバ変数がAssignableであることを 要求しない
  107. 107. 例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;} Copy And Swap
  108. 108. 例外安全にするための技法 ● RAII● 「リソースの確保は初期化である」
  109. 109. 例外安全にするための技法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; } //リソース管理のためにコードが複雑に}
  110. 110. 例外安全にするための技法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も分散してしまった}
  111. 111. 例外安全にするための技法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; } //対応していない}
  112. 112. 例外安全にするための技法 ● RAII● 「リソースの確保は初期化である」● コンストラクタでリソースを確保● デストラクタでリソースを破棄
  113. 113. 例外安全にするための技法 ● RAII● 「リソースの確保は初期化である」● コンストラクタでリソースを確保● デストラクタでリソースを破棄 対応!!
  114. 114. 例外安全にするための技法 ● RAIIclass RAIIClass{ RAIIClass(Resource r) : r_(r) {} RAIIClass(Args as) : r_(CreateResource(as)) {} ~RAIIClass() { DisposeResource(r_); }private: Resource r_;};
  115. 115. 例外安全にするための技法 ● RAII● これをメモリ管理に使ったのがいわ ゆるスマートポインタ
  116. 116. 例外安全にするための技法 ● RAII● これをメモリ管理に使ったのがいわ ゆるスマートポインタ● プログラマはリソース解放のめんど くささから解放される!
  117. 117. 例外安全にするための技法 ● RAII● これをメモリ管理に使ったのがいわ ゆるスマートポインタ● プログラマはリソース解放のめんど くささから解放される!● 例外安全とか考えなくても使うべ き!
  118. 118. 例外安全にするための技法 ● RAII● これがどのように例外安全性の点で 重宝されるのか?
  119. 119. 例外安全にするための技法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; }}
  120. 120. 例外安全にするための技法 ● RAII● Deleteのために例外をCatchしなく ちゃいけなかった
  121. 121. 例外安全にするための技法 ● RAII● Deleteのために例外をCatchしなく ちゃいけなかった● でもRAIIなクラスを使うと、Delete は自動化できる
  122. 122. 例外安全にするための技法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; }}
  123. 123. 例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp = data_; try { data_.reset(new T[n]); こうなる read_data(data_, n); } catch (...) { data_ = tmp; }}
  124. 124. 例外安全にするための技法 ● RAII● どこで起きるかわからないような例 外によって関数を抜けるときも、そ の時に自動でリソースが解放され る!● リソース解放のためのTry-Catchは要 らなくなる!
  125. 125. 例外安全にするための技法 ● RAII● どこで起きるかわからないような例 外によって関数を抜けるときも、そ の時に自動でリソースが解放され る!● リソース解放のためのTry-Catchは要 らなくなる!(強い例外安全保証の ためのは必要・・・)
  126. 126. 例外安全にするための技法 ● SwapとRAII
  127. 127. 例外安全にするための技法 ● SwapとRAII1.Swapはロールバック用のTry- Catchを不要にする。
  128. 128. 例外安全にするための技法 ● SwapとRAII1.Swapはロールバック用のTry- Catchを不要にする。2.RAIIはリソース管理用のTry- Catchを不要にする。
  129. 129. 例外安全にするための技法 ● SwapとRAII1.Swapはロールバック用のTry- Catchを不要にする。2.RAIIはリソース管理用のTry- Catchを不要にする。3.例外中立のために、例外が起き てもいじらないで伝える。
  130. 130. 例外安全にするための技法 ● SwapとRAII 合体
  131. 131. 例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp(new T[n]); read_data(tmp_, n); std::swap(data_, tmp);}
  132. 132. 例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp(new T[n]); read_data(tmp_, n); std::swap(data_, tmp);} おわかりいただけただろうか
  133. 133. 例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp(new T[n]); read_data(tmp_, n); std::swap(data_, tmp);} おわかりいただけただろうか ではもう一度・・・
  134. 134. 例外安全にするための技法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; }}
  135. 135. 例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp(new T[n]); read_data(tmp_, n); std::swap(data_, tmp);}
  136. 136. 例外安全にするための技法 ● SwapとRAII● 適切なコードを書けば、例外安全 性のためにTry-Catchを書く必要 はない。
  137. 137. 例外安全にするための技法 ● デストラクタで例外
  138. 138. 例外安全にするための技法 ● デストラクタで例外● 禁止!
  139. 139. 例外安全にするための技法 ● デストラクタで例外● 禁止!● 配列を安全にできない● 例外が起きたときにスタックの巻 き戻しを安全にできない● 全ての例外安全の努力が水の泡!
  140. 140. 5.まとめ
  141. 141. まとめ● 例外安全なコードを書くには
  142. 142. まとめ● 例外安全なコードを書くには● 例外を投げる部分と投げない部分を 分離して、例外のある処理の成功を 確認してから、状態を変更する。● RAIIを使用してリソースの管理を自 動化する
  143. 143. まとめ● 今回の内容はほとんど全て 「Exceptional C++」 (ハーブ・サッター著) に書かれています● あと「C++ Coding Standard」も
  144. 144. おしまい。

×