144
例外安全入門 @hotwatermorning

Sapporocpp#2 exception-primer

Embed Size (px)

DESCRIPTION

sapporo.cpp #2

Citation preview

Page 1: Sapporocpp#2 exception-primer

例外安全入門@hotwatermorning

Page 2: Sapporocpp#2 exception-primer

自己紹介

● @hotwatermorning● はてなid:heisesswasser● 大学生● DTMやってます● C++が好きです● 「プログラミングの魔導少女」では

「RangeとPStade.Oven」という記事を書かせていただきました。

Page 3: Sapporocpp#2 exception-primer

C++erなら

Page 4: Sapporocpp#2 exception-primer

誰しもメモリーリークに

Page 5: Sapporocpp#2 exception-primer

悩まされたことがあるはず

Page 6: Sapporocpp#2 exception-primer

今回のセッションは

Page 7: Sapporocpp#2 exception-primer

その悩みを

Page 8: Sapporocpp#2 exception-primer

軽減する

Page 9: Sapporocpp#2 exception-primer

軽減する

かもしれない

Page 10: Sapporocpp#2 exception-primer

本日のレシピ

1.例外安全とは?

2.例外安全性の種類

3.例外安全なコードを書くには

4.例外安全にするための技法

Page 11: Sapporocpp#2 exception-primer

本日のレシピ

1.例外安全とは?

2.例外安全性の種類

3.例外安全なコードを書くには

4.例外安全にするための技法

●いわゆる、エラーハンドリング/例外ハンドリング関連の内容は含んでおりません。

Page 12: Sapporocpp#2 exception-primer

本日のレシピ

1.例外安全とは?

2.例外安全性の種類

3.例外安全なコードを書くには

4.例外安全にするための技法

●いわゆる、エラーハンドリング/例外ハンドリング関連の内容は含んでおりません。●というかそれは僕も知りたいので誰か教えt(ry

Page 13: Sapporocpp#2 exception-primer

1.例外安全とは?

Page 14: Sapporocpp#2 exception-primer

例外安全とは?

「あるコード内を実行中の失敗が、メモリリーク、格納データの不整合、不正な出力などの有害な効果を生じないとき、そのコード片は例外安全であると言う。」“例外処理”

http://ja.wikipedia.org/wiki/%E4%BE%8B%E5%A4%96%E5%87%A6%E7%90%86

Page 15: Sapporocpp#2 exception-primer

例外安全とは?

「例外安全なプログラミングとは、 例外を投げる可能性があるコードが実際に例外を投げた場合に、 プログラムの状態が壊れずリソースもリークしないように作るプログラミングのことを言います。 」“例外安全なプログラミング”

http://www.kmonos.net/alang/d/2.0/exception-safe.html

Page 16: Sapporocpp#2 exception-primer

例外安全とは?

● 例外が発生しても適切に対処できるようなコードを書く。

● 適切に対処しないと・・・‣ メモリリーク、リソースリーク‣ 中途半端な操作の完了‣ デストラクタから例外が投げられる

と・・・

Page 17: Sapporocpp#2 exception-primer

例外安全でないコードの例(1)

void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; read_data(data_, n);}

Page 18: Sapporocpp#2 exception-primer

例外安全でないコードの例(1)

void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);}

Page 19: Sapporocpp#2 exception-primer

例外安全でないコードの例(1)

void SampleClass::ReadData(int n){ delete [] data_; data_ = new T[n]; //←ここや read_data(data_, n);//←ここで}

Page 20: Sapporocpp#2 exception-primer

例外安全でないコードの例(1)

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

Page 21: Sapporocpp#2 exception-primer

例外安全でないコードの例(1)

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

SampleClassの状態が壊れてしまう!!

Page 22: Sapporocpp#2 exception-primer

例外安全でないコードの例(2)

SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_; data3_ = rhs.data3_; data4_ = rhs.data4_; return *this;}

Page 23: Sapporocpp#2 exception-primer

例外安全でないコードの例(2)

SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_;//このうち data3_ = rhs.data3_; data4_ = rhs.data4_; return *this;}

Page 24: Sapporocpp#2 exception-primer

例外安全でないコードの例(2)

SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_; data2_ = rhs.data2_;//このうち data3_ = rhs.data3_;//どれかで data4_ = rhs.data4_; return *this;}

Page 25: Sapporocpp#2 exception-primer

例外安全でないコードの例(2)

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

Page 26: Sapporocpp#2 exception-primer

例外安全でないコードの例(2)

SampleClass2 & SampleClass2::operator= (SampleClass2 const &rhs){ data1_ = rhs.data1_;//これはどうなる? data2_ = rhs.data2_;//このうち data3_ = rhs.data3_;//どれかで data4_ = rhs.data4_;//例外が起きたら return *this;}

Page 27: Sapporocpp#2 exception-primer

例外安全なコードじゃないと

Page 28: Sapporocpp#2 exception-primer

例外が起きたときにきちんと対処できない

Page 29: Sapporocpp#2 exception-primer

よし、

Page 30: Sapporocpp#2 exception-primer

例外安全なコードを書こう!

Page 31: Sapporocpp#2 exception-primer

と、そのまえに

Page 32: Sapporocpp#2 exception-primer

2.例外安全性の種類

Page 33: Sapporocpp#2 exception-primer

例外安全性の種類

●例外を投げない保証●強い例外安全●基本的な例外安全●例外安全保証なし

例外安全性

Page 34: Sapporocpp#2 exception-primer

例外安全性の種類

●例外を投げない保証●強い例外安全●基本的な例外安全●例外安全保証なし

但し・・・

Page 35: Sapporocpp#2 exception-primer

例外安全性の種類

●例外を投げない保証●強い例外安全●基本的な例外安全●例外安全保証なし

コスト…

Page 36: Sapporocpp#2 exception-primer

例外安全性の種類

● 例外安全保証なし

Page 37: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 38: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 39: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 40: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 41: Sapporocpp#2 exception-primer

例外安全性の種類

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

● 副作用は出るかもしれない(データの変更や出力など。)

Page 42: Sapporocpp#2 exception-primer

例外安全性の種類

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

● コンテナの中で例外が発生した場合、一貫性は保っているが、予測可能な状態とは限らない。

● なのでその後、削除や再利用はできる。

Page 43: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 44: Sapporocpp#2 exception-primer

例外安全性の種類

● 強い例外安全の保証

Page 45: Sapporocpp#2 exception-primer

例外安全性の種類

● 強い例外安全の保証● 例外が起きたなら、全ての変更はロールバックされる

● 完全な成功か、例外による完全な失敗かの2つの状況になる

Page 46: Sapporocpp#2 exception-primer

例外安全性の種類

● 強い例外安全の保証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; }}

Page 47: Sapporocpp#2 exception-primer

例外安全性の種類

● 例外を投げない保証(no throw)

Page 48: Sapporocpp#2 exception-primer

例外安全性の種類

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

Page 49: Sapporocpp#2 exception-primer

例外安全性の種類

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

● 例)基本データ型の代入などは例外が起きない

Page 50: Sapporocpp#2 exception-primer

例外安全性の種類

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

● 例)基本データ型の代入などは例外が起きない

● 呼び出し先も例外を投げない保証ができなければならない

Page 51: Sapporocpp#2 exception-primer

例外安全性の種類

● 例外を投げない保証(no throw)void foo(){ int n1 = 0x10; int n2 = n1; int *pn1 = &n1; int *pn2 = pn1;}

Page 52: Sapporocpp#2 exception-primer

3.例外安全なコードを書くには

Page 53: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

Page 54: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● どゆこと?

Page 55: Sapporocpp#2 exception-primer

例外安全なコードを書くには

int baz() { return bar() * bar(); }void foo(){ int n1 = 1; //例外安全なコード int n2 = bar(); //例外安全でないコード int n3 = 3; //例外安全なコード int n4 = baz(); //例外安全でないコード}

Page 56: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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; }}

(さっきの「強い例外安全」のサンプルコード)

Page 57: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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; }}

(さっきの「強い例外安全」のサンプルコード)

Page 58: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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; }}

(さっきの「強い例外安全」のサンプルコード)

Page 59: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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...//完全な例外安全とは } //言えなくなる}

(さっきの「強い例外安全」のサンプルコード)

Page 60: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● 呼び出し先の例外安全性にも左右されてしまう。

Page 61: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● 呼び出し先の例外安全性にも左右されてしまう。

● プログラムの中で基本的な機能であるほど、しっかりした例外安全性を実装していなければならない。(コンテナなど)

Page 62: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● 呼び出し先の例外安全性にも左右されてしまう。

● プログラムの中で基本的な機能であるほど、しっかりした例外安全性を実装していなければならない。(コンテナなど)

● 標準のコンテナは大体強い保証を満たす

Page 63: Sapporocpp#2 exception-primer

例外安全なコードを書くには

● それぞれの関数で例外を投げうる部分と投げない部分を明確に分離する。

● 本来の処理の成功を確認した時点で、例外を投げない部分を使って、状態の変更と後処理を行うようにする。

Page 64: Sapporocpp#2 exception-primer

例外安全なコードを書くには

● 例外中立について

Page 65: Sapporocpp#2 exception-primer

例外安全なコードを書くには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; }}

Page 66: Sapporocpp#2 exception-primer

例外安全なコードを書くには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; } //例外安全に書けましたね!}

Page 67: Sapporocpp#2 exception-primer

例外安全なコードを書くには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; } //でもいざ例外が起きたときに}

Page 68: Sapporocpp#2 exception-primer

例外安全なコードを書くには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の呼び出し元は} //ここで起きた例外をなにも知らない

Page 69: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

Page 70: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● コンテナでは、自分の行った範囲の例外は安全に処理できるけど、他は無理。

Page 71: Sapporocpp#2 exception-primer

例外安全なコードを書くには

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

● コンテナでは、自分の行った範囲の例外は安全に処理できるけど、他は無理。

● 適切に処理できるところまでは例外を正しく伝える。

Page 72: Sapporocpp#2 exception-primer

例外安全なコードを書くには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; }}

Page 73: Sapporocpp#2 exception-primer

例外安全なコードを書くには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; //正しくはこう。 }}

Page 74: Sapporocpp#2 exception-primer

例外安全なコードを書くには

● 単一の関数にatomicでない複数の処理があると、例外安全の強い保証をするのは不可能。

● 例)std::coutに出力してからstd::cerrに出力

Page 75: Sapporocpp#2 exception-primer

4.例外安全にするための技法

Page 76: Sapporocpp#2 exception-primer

これまでのことは

Page 77: Sapporocpp#2 exception-primer

いまから紹介する2つのテクニックの

Page 78: Sapporocpp#2 exception-primer

布石に過ぎなかった

Page 79: Sapporocpp#2 exception-primer

布石に過ぎなかった\ばばーん/

Page 80: Sapporocpp#2 exception-primer

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

● いままで説明してきたことを上手く実装するためのテクニック

Page 81: Sapporocpp#2 exception-primer

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

● いままで説明してきたことを上手く実装するためのテクニック

● Swap

Page 82: Sapporocpp#2 exception-primer

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

● いままで説明してきたことを上手く実装するためのテクニック

● Swap● RAII(Resource Acquisition Is Initialization)

Page 83: Sapporocpp#2 exception-primer

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

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

Page 84: Sapporocpp#2 exception-primer

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

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

● Swapは、例外を投げない保証を必ず守る

Page 85: Sapporocpp#2 exception-primer

例外安全にするための技法template<typename T>void swap(T &lhs, T &rhs){ T tmp = lhs; lhs = rhs; rhs = tmp;}

Page 86: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //頑張って強い例外安全にしてるけど}

Page 87: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //ロールバック用にいろいろ書いてて}

Page 88: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //例外を投げる部分が}

Page 89: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //例外を投げない部分に囲まれて}

Page 90: Sapporocpp#2 exception-primer

例外安全にするための技法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も入り乱れて}

Page 91: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //ちょっと複雑・・・}

Page 92: Sapporocpp#2 exception-primer

例外安全なコードを書くには

● それぞれの関数で例外を投げうる部分と投げない部分を明確に分離する。

● 本来の処理の成功を確認した時点で、例外を投げない部分を使って、状態の変更と後処理を行うようにする。

Page 93: Sapporocpp#2 exception-primer

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

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

Page 94: Sapporocpp#2 exception-primer

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

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

例外を投げうる部分投げない部分

Page 95: Sapporocpp#2 exception-primer

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

std::swap(data_, tmp);//終わったら入れ替え delete [] tmp; //後処理}

Page 96: Sapporocpp#2 exception-primer

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

● Swap● 例外を投げうる部分と投げない部分を明確に分離

● 例外を投げる部分が成功したら例外を投げない部分で状態を変更

● ロールバック用のTry-Catchは要らなくなる!

Page 97: Sapporocpp#2 exception-primer

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

● Swap● 例外を投げうる部分と投げない部分を明確に分離

● 例外を投げる部分が成功したら例外を投げない部分で状態を変更

● ロールバック用のTry-Catchは要らなくなる!

(リソース管理用のは必要・・・)

Page 98: Sapporocpp#2 exception-primer

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

● Swap● 自作クラスでも、

Copy-Constructableなクラスならswapを実装する。

Page 99: Sapporocpp#2 exception-primer

例外安全にするための技法void SampleClass::swap(SampleClass &rhs){ //標準のswap関数や std::swap(data_, rhs.data_); //それ用のswap関数などで other_data_.swap(rhs.other_data_);}

Page 100: Sapporocpp#2 exception-primer

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

● Swap● 自作クラスでも、

Copy-Constructableなクラスならswapを実装する。

● SampleClass

(SampleClass const &rhs);

Page 101: Sapporocpp#2 exception-primer

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

● Swap● 自作クラスでも、

Copy-Constructableなクラスならswapを実装する。

● 実装すると・・・

Page 102: Sapporocpp#2 exception-primer

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

● Swap● 自作クラスでも、

Copy-Constructableなクラスならswapを実装する。

● Assignableなクラスに出来る。● SampleClass &

operator=(SampleClass const &)

Page 103: Sapporocpp#2 exception-primer

例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ //コピーコンストラクタ SampleClass tmp(rhs); //swap tmp.swap(*this); return *this;}

Page 104: Sapporocpp#2 exception-primer

例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ //コピーコンストラクタ SampleClass tmp(rhs); //swap tmp.swap(*this); return *this;}

さらにまとめて書くと

Page 105: Sapporocpp#2 exception-primer

例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;}

Page 106: Sapporocpp#2 exception-primer

例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;}メンバ変数がAssignableであることを要求しない

Page 107: Sapporocpp#2 exception-primer

例外安全にするための技法SampleClass & SampleClass::operator= (SampleClass const &rhs){ SampleClass(rhs).swap(*this); return *this;}

Copy And Swap

Page 108: Sapporocpp#2 exception-primer

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

● RAII● 「リソースの確保は初期化である」

Page 109: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //リソース管理のためにコードが複雑に}

Page 110: Sapporocpp#2 exception-primer

例外安全にするための技法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も分散してしまった}

Page 111: Sapporocpp#2 exception-primer

例外安全にするための技法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; } //対応していない}

Page 112: Sapporocpp#2 exception-primer

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

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

Page 113: Sapporocpp#2 exception-primer

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

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

対応!!対応!!

Page 114: Sapporocpp#2 exception-primer

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

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

~RAIIClass() { DisposeResource(r_); }private: Resource r_;};

Page 115: Sapporocpp#2 exception-primer

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

● RAII● これをメモリ管理に使ったのがいわゆるスマートポインタ

Page 116: Sapporocpp#2 exception-primer

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

● RAII● これをメモリ管理に使ったのがいわゆるスマートポインタ

● プログラマはリソース解放のめんどくささから解放される!

Page 117: Sapporocpp#2 exception-primer

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

● RAII● これをメモリ管理に使ったのがいわゆるスマートポインタ

● プログラマはリソース解放のめんどくささから解放される!

● 例外安全とか考えなくても使うべき!

Page 118: Sapporocpp#2 exception-primer

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

● RAII● これがどのように例外安全性の点で重宝されるのか?

Page 119: Sapporocpp#2 exception-primer

例外安全にするための技法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; }}

Page 120: Sapporocpp#2 exception-primer

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

● RAII● Deleteのために例外をCatchしなくちゃいけなかった

Page 121: Sapporocpp#2 exception-primer

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

● RAII● Deleteのために例外をCatchしなくちゃいけなかった

● でもRAIIなクラスを使うと、Deleteは自動化できる

Page 122: Sapporocpp#2 exception-primer

例外安全にするための技法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; }}

これがこれが

Page 123: Sapporocpp#2 exception-primer

例外安全にするための技法void SampleClass::ReadData(int n){ shared_array<T> tmp = data_; try { data_.reset(new T[n]); read_data(data_, n); } catch (...) { data_ = tmp; }}

こうなるこうなる

Page 124: Sapporocpp#2 exception-primer

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

● RAII● どこで起きるかわからないような例外によって関数を抜けるときも、その時に自動でリソースが解放される!

● リソース解放のためのTry-Catchは要らなくなる!

Page 125: Sapporocpp#2 exception-primer

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

● RAII● どこで起きるかわからないような例外によって関数を抜けるときも、その時に自動でリソースが解放される!

● リソース解放のためのTry-Catchは要らなくなる!(強い例外安全保証のためのは必要・・・)

Page 126: Sapporocpp#2 exception-primer

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

● SwapとRAII

Page 127: Sapporocpp#2 exception-primer

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

● SwapとRAII

1.Swapはロールバック用のTry-Catchを不要にする。

Page 128: Sapporocpp#2 exception-primer

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

● SwapとRAII

1.Swapはロールバック用のTry-Catchを不要にする。

2.RAIIはリソース管理用のTry-Catchを不要にする。

Page 129: Sapporocpp#2 exception-primer

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

● SwapとRAII

1.Swapはロールバック用のTry-Catchを不要にする。

2.RAIIはリソース管理用のTry-Catchを不要にする。

3.例外中立のために、例外が起きてもいじらないで伝える。

Page 130: Sapporocpp#2 exception-primer

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

● SwapとRAII

合体合体

Page 131: Sapporocpp#2 exception-primer

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

Page 132: Sapporocpp#2 exception-primer

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

おわかりいただけただろうかおわかりいただけただろうか

Page 133: Sapporocpp#2 exception-primer

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

おわかりいただけただろうかおわかりいただけただろうかではもう一度・・・ではもう一度・・・

Page 134: Sapporocpp#2 exception-primer

例外安全にするための技法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; }}

Page 135: Sapporocpp#2 exception-primer

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

Page 136: Sapporocpp#2 exception-primer

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

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

Page 137: Sapporocpp#2 exception-primer

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

● デストラクタで例外

Page 138: Sapporocpp#2 exception-primer

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

● デストラクタで例外●禁止!

Page 139: Sapporocpp#2 exception-primer

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

● デストラクタで例外●禁止!●配列を安全にできない● 例外が起きたときにスタックの巻き戻しを安全にできない

● 全ての例外安全の努力が水の泡!

Page 140: Sapporocpp#2 exception-primer

5.まとめ

Page 141: Sapporocpp#2 exception-primer

まとめ

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

Page 142: Sapporocpp#2 exception-primer

まとめ

● 例外安全なコードを書くには● 例外を投げる部分と投げない部分を分離して、例外のある処理の成功を確認してから、状態を変更する。

● RAIIを使用してリソースの管理を自動化する

Page 143: Sapporocpp#2 exception-primer

まとめ

● 今回の内容はほとんど全て「Exceptional C++」

(ハーブ・サッター著)

に書かれています● あと「C++ Coding Standard」も

Page 144: Sapporocpp#2 exception-primer

おしまい。