More Related Content
Similar to Sapporocpp#2 exception-primer (12)
More from Kohsuke Yuasa (9)
Sapporocpp#2 exception-primer
- 2. 自己紹介
●
@hotwatermorning
●
はてなid:heisesswasser
●
大学生
●
DTMやってます
●
C++が好きです
●
「プログラミングの魔導少女」では
「RangeとPStade.Oven」という記事を書かせ
ていただきました。
- 16. 例外安全とは?
●
例外が発生しても適切に対処できるよう
なコードを書く。
●
適切に対処しないと・・・
‣ メモリリーク、リソースリーク
‣ 中途半端な操作の完了
‣ デストラクタから例外が投げられる
と・・・
- 22. 例外安全でないコードの例(2)
SampleClass2 &
SampleClass2::operator=
(SampleClass2 const &rhs)
{
data1_ = rhs.data1_;
data2_ = rhs.data2_;
data3_ = rhs.data3_;
data4_ = rhs.data4_;
return *this;
}
- 23. 例外安全でないコードの例(2)
SampleClass2 &
SampleClass2::operator=
(SampleClass2 const &rhs)
{
data1_ = rhs.data1_;
data2_ = rhs.data2_;//このうち
data3_ = rhs.data3_;
data4_ = rhs.data4_;
return *this;
}
- 24. 例外安全でないコードの例(2)
SampleClass2 &
SampleClass2::operator=
(SampleClass2 const &rhs)
{
data1_ = rhs.data1_;
data2_ = rhs.data2_;//このうち
data3_ = rhs.data3_;//どれかで
data4_ = rhs.data4_;
return *this;
}
- 25. 例外安全でないコードの例(2)
SampleClass2 &
SampleClass2::operator=
(SampleClass2 const &rhs)
{
data1_ = rhs.data1_;
data2_ = rhs.data2_;//このうち
data3_ = rhs.data3_;//どれかで
data4_ = rhs.data4_;//例外が起きたら
return *this;
}
- 26. 例外安全でないコードの例(2)
SampleClass2 &
SampleClass2::operator=
(SampleClass2 const &rhs)
{
data1_ = rhs.data1_;//これはどうなる?
data2_ = rhs.data2_;//このうち
data3_ = rhs.data3_;//どれかで
data4_ = rhs.data4_;//例外が起きたら
return *this;
}
- 37. 例外安全性の種類
●
例外安全保証なし
●
その関数や呼び出し先で例外が起きると
リソースがリークしてしまうかもしれな
い・・・
- 38. 例外安全性の種類
●
例外安全保証なし
void SampleClass::ReadData(int n)
{
delete [] data_;
data_ = new T[n];
read_data(data_, n);
}
- 40. 例外安全性の種類
●
基本的な例外安全の保証
●
例外が投げられても、いかなるリソース
もリークしない。
- 41. 例外安全性の種類
●
基本的な例外安全の保証
●
例外が投げられても、いかなるリソース
もリークしない。
●
副作用は出るかもしれない(データの変
更や出力など。)
- 42. 例外安全性の種類
●
基本的な例外安全の保証
●
例外が投げられても、いかなるリソース
もリークしない。
●
コンテナの中で例外が発生した場合、一
貫性は保っているが、予測可能な状態と
は限らない。
●
なのでその後、削除や再利用はできる。
- 43. 例外安全性の種類
●
基本的な例外安全の保証
void SampleClass::ReadData(int n)
{
delete [] data_; data_ = 0;
try {
data_ = new T[n];
read_data(data_, n);
} catch(...) {
delete [] data_; data_ = 0;
}
}
- 45. 例外安全性の種類
●
強い例外安全の保証
●
例外が起きたなら、全ての変更は
ロールバックされる
●
完全な成功か、例外による完全な失
敗かの2つの状況になる
- 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;
}
}
- 48. 例外安全性の種類
●
例外を投げない保証(no throw)
●
操作は全て正しく完了されることが
保証される。
- 49. 例外安全性の種類
●
例外を投げない保証(no throw)
●
操作は全て正しく完了されることが
保証される。
●
例)基本データ型の代入などは例外
が起きない
- 50. 例外安全性の種類
●
例外を投げない保証(no throw)
●
操作は全て正しく完了されることが
保証される。
●
例)基本データ型の代入などは例外
が起きない
●
呼び出し先も例外を投げない保証が
できなければならない
- 51. 例外安全性の種類
●
例外を投げない保証(no throw)
void foo()
{
int n1 = 0x10;
int n2 = n1;
int *pn1 = &n1;
int *pn2 = pn1;
}
- 55. 例外安全なコードを書くには
int baz() { return bar() * bar(); }
void foo()
{
int n1 = 1; //例外安全なコード
int n2 = bar(); //例外安全でないコード
int n3 = 3; //例外安全なコード
int n4 = baz(); //例外安全でないコード
}
- 60. 例外安全なコードを書くには
●
ある実行パス中の例外安全性は、その工
程で一番低いものになる。
●
呼び出し先の例外安全性にも左右されて
しまう。
- 61. 例外安全なコードを書くには
●
ある実行パス中の例外安全性は、その工
程で一番低いものになる。
●
呼び出し先の例外安全性にも左右されて
しまう。
●
プログラムの中で基本的な機能であるほ
ど、しっかりした例外安全性を実装して
いなければならない。(コンテナなど)
- 62. 例外安全なコードを書くには
●
ある実行パス中の例外安全性は、その工
程で一番低いものになる。
●
呼び出し先の例外安全性にも左右されて
しまう。
●
プログラムの中で基本的な機能であるほ
ど、しっかりした例外安全性を実装して
いなければならない。(コンテナなど)
●
標準のコンテナは大体強い保証を満たす
- 63. 例外安全なコードを書くには
●
それぞれの関数で例外を投げうる部分と
投げない部分を明確に分離する。
●
本来の処理の成功を確認した時点で、例
外を投げない部分を使って、状態の変更
と後処理を行うようにする。
- 69. 例外安全なコードを書くには
●
例外中立について
●
コンテナなどで例外が起きると、その例
外が起きるまでのコンテキストを知って
いるのは呼び出し元だけ。
- 70. 例外安全なコードを書くには
●
例外中立について
●
コンテナなどで例外が起きると、その例
外が起きるまでのコンテキストを知って
いるのは呼び出し元だけ。
●
コンテナでは、自分の行った範囲の例外
は安全に処理できるけど、他は無理。
- 71. 例外安全なコードを書くには
●
例外中立について
●
コンテナなどで例外が起きると、その例
外が起きるまでのコンテキストを知って
いるのは呼び出し元だけ。
●
コンテナでは、自分の行った範囲の例外
は安全に処理できるけど、他は無理。
●
適切に処理できるところまでは例外を正
しく伝える。
- 74. 例外安全なコードを書くには
●
単一の関数にatomicでない複数の処理が
あると、例外安全の強い保証をするのは
不可能。
●
例)std::coutに出力してからstd::cerr
に出力
- 82. 例外安全にするための技法
●
いままで説明してきたことを上手く実装
するためのテクニック
●
Swap
●
RAII(Resource Acquisition Is
Initialization)
- 83. 例外安全にするための技法
●
Swap
●
実体を他に作って、全て成功した
ら、変更したいリソースとSwapする
- 84. 例外安全にするための技法
●
Swap
●
実体を他に作って、全て成功した
ら、変更したいリソースとSwapする
●
Swapは、例外を投げない保証を必ず
守る
- 92. 例外安全なコードを書くには
●
それぞれの関数で例外を投げうる部分と
投げない部分を明確に分離する。
●
本来の処理の成功を確認した時点で、例
外を投げない部分を使って、状態の変更
と後処理を行うようにする。
- 94. 例外安全にするための技法
●
Swap
●
実体を他に作って、全て成功した
ら、変更したいリソースとSwap
例外を投げうる部分
投げない部分
- 96. 例外安全にするための技法
●
Swap
●
例外を投げうる部分と投げない部分を明
確に分離
●
例外を投げる部分が成功したら例外を投
げない部分で状態を変更
●
ロールバック用のTry-Catchは要らなく
なる!
- 97. 例外安全にするための技法
●
Swap
●
例外を投げうる部分と投げない部分を明
確に分離
●
例外を投げる部分が成功したら例外を投
げない部分で状態を変更
●
ロールバック用のTry-Catchは要らなく
なる!
(リソース管理用のは必要・・・)
- 98. 例外安全にするための技法
●
Swap
●
自作クラスでも、
Copy-Constructableなクラスなら
swapを実装する。
- 100. 例外安全にするための技法
●
Swap
●
自作クラスでも、
Copy-Constructableなクラスなら
swapを実装する。
●
SampleClass
(SampleClass const &rhs);
- 101. 例外安全にするための技法
●
Swap
●
自作クラスでも、
Copy-Constructableなクラスなら
swapを実装する。
●
実装すると・・・
- 102. 例外安全にするための技法
●
Swap
●
自作クラスでも、
Copy-Constructableなクラスなら
swapを実装する。
●
Assignableなクラスに出来る。
●
SampleClass &
operator=(SampleClass const &)
- 103. 例外安全にするための技法
SampleClass &
SampleClass::operator=
(SampleClass const &rhs)
{
//コピーコンストラクタ
SampleClass tmp(rhs);
//swap
tmp.swap(*this);
return *this;
}
- 104. 例外安全にするための技法
SampleClass &
SampleClass::operator=
(SampleClass const &rhs)
{
//コピーコンストラクタ
SampleClass tmp(rhs);
//swap
tmp.swap(*this); さらにまとめて
return *this; 書くと
}
- 106. 例外安全にするための技法
SampleClass &
SampleClass::operator=
(SampleClass const &rhs)
{
SampleClass(rhs).swap(*this);
return *this;
}
メンバ変数がAssignableであることを
要求しない
- 107. 例外安全にするための技法
SampleClass &
SampleClass::operator=
(SampleClass const &rhs)
{
SampleClass(rhs).swap(*this);
return *this;
}
Copy And Swap
- 112. 例外安全にするための技法
●
RAII
●
「リソースの確保は初期化である」
●
コンストラクタでリソースを確保
●
デストラクタでリソースを破棄
- 113. 例外安全にするための技法
●
RAII
●
「リソースの確保は初期化である」
●
コンストラクタでリソースを確保
●
デストラクタでリソースを破棄
対応!!
- 114. 例外安全にするための技法
●
RAII
class RAIIClass
{
RAIIClass(Resource r) : r_(r) {}
RAIIClass(Args as) : r_(CreateResource(as))
{}
~RAIIClass() { DisposeResource(r_); }
private:
Resource r_;
};
- 116. 例外安全にするための技法
●
RAII
●
これをメモリ管理に使ったのがいわ
ゆるスマートポインタ
●
プログラマはリソース解放のめんど
くささから解放される!
- 117. 例外安全にするための技法
●
RAII
●
これをメモリ管理に使ったのがいわ
ゆるスマートポインタ
●
プログラマはリソース解放のめんど
くささから解放される!
●
例外安全とか考えなくても使うべ
き!
- 121. 例外安全にするための技法
●
RAII
●
Deleteのために例外をCatchしなく
ちゃいけなかった
●
でもRAIIなクラスを使うと、Delete
は自動化できる
- 124. 例外安全にするための技法
●
RAII
●
どこで起きるかわからないような例
外によって関数を抜けるときも、そ
の時に自動でリソースが解放され
る!
●
リソース解放のためのTry-Catchは要
らなくなる!
- 125. 例外安全にするための技法
●
RAII
●
どこで起きるかわからないような例
外によって関数を抜けるときも、そ
の時に自動でリソースが解放され
る!
●
リソース解放のためのTry-Catchは要
らなくなる!(強い例外安全保証の
ためのは必要・・・)
- 127. 例外安全にするための技法
●
SwapとRAII
1.Swapはロールバック用のTry-
Catchを不要にする。
- 128. 例外安全にするための技法
●
SwapとRAII
1.Swapはロールバック用のTry-
Catchを不要にする。
2.RAIIはリソース管理用のTry-
Catchを不要にする。
- 129. 例外安全にするための技法
●
SwapとRAII
1.Swapはロールバック用のTry-
Catchを不要にする。
2.RAIIはリソース管理用のTry-
Catchを不要にする。
3.例外中立のために、例外が起き
てもいじらないで伝える。
- 136. 例外安全にするための技法
●
SwapとRAII
●
適切なコードを書けば、例外安全
性のためにTry-Catchを書く必要
はない。
- 139. 例外安全にするための技法
●
デストラクタで例外
●
禁止!
●
配列を安全にできない
●
例外が起きたときにスタックの巻
き戻しを安全にできない
●
全ての例外安全の努力が水の泡!
- 142. まとめ
●
例外安全なコードを書くには
●
例外を投げる部分と投げない部分を
分離して、例外のある処理の成功を
確認してから、状態を変更する。
●
RAIIを使用してリソースの管理を自
動化する
- 143. まとめ
●
今回の内容はほとんど全て
「Exceptional C++」
(ハーブ・サッター著)
に書かれています
●
あと「C++ Coding Standard」も