Exceptional C++

Exceptional C++―47のクイズ形式によるプログラム問題と解法 (C++ in‐Depth Series)

Exceptional C++―47のクイズ形式によるプログラム問題と解法 (C++ in‐Depth Series)

  • 作者: ハーブサッター,浜田光之,Harb Sutter,浜田真理
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/11
  • メディア: 単行本
  • 購入: 9人 クリック: 134回
  • この商品を含むブログ (63件) を見る
を購入して一通り読んでみた。評判通り例外安全性について勉強するには良書。

例外安全性

例外安全について考慮すべき点は3点

例外中立
例外を処理しない場合はそのまま呼び出し側に伝える。
基本的保証
メモリリークを起こさないことを保証する。
強い保証
関数内で例外が投げられた場合に、一貫性を保つ事を保証する。

例外中立と基本的保証は絶対に守らなければならず、パフォーマンス上の問題が無ければ強い保証もするべきである。
強い保証を保つ為に有用なテクニックが「例外を投げないswap」の利用。
swapはEffective C++やCoding Standardsでも推奨されていたけど、本書でようやくその基本的な考え方を理解できた気がする。

基本的な考え方は「コピーに対して変更を行い、最後にswap」
もし、直接thisに対して変更を行うとその途中で例外が起こった場合にコミットロールバックができないので強い保証ができない。

class Foo
{
public:
  //...
  void swap(Foo other&) throw() { // swapは絶対に例外を投げてはいけない。
    //...
  }
  void f()
  {
    Foo temp(*this);
    // tempに対していろいろ操作を行う。
    
    // もし例外が投げられなかったら自身とswap
    // 例外が投げられたらthis自身は変化しない。
    swap(temp);
  }
  //...
};

もう一つ勉強になったのは「例外安全性を考慮する場合はクライアントがどうコードを書くかを考えなければならない」ということ。
stl::listのpop_frontやpop_backの戻り値がなぜvoidなのか?」を例にしてみると、

list<foo> a;

// ...

foo b = a.pop_front(); // <- ここ

もしpop_frontやpop_backが値を返す場合はa.pop_frontの戻り値をbに代入する場合にコピーコンストラクタが例外を投げる場合がありえる。そして、コミットロールバックすべき地点はfoo b = a.pop_front()の一行前の状態だけど、aの要素は削除されてしまっている。よって強い保証はできないということになる。

関数の外側で発生する例外なんて考えたことも無かったなぁ。

その他

その他勉強になったことを箇条書きで

  • テンプレートコンストラクタはコピーコンストラクタではない。
  • i++ を ++iにする最適化はされないことが多い。できるだけ++iを使うようにした方が効率的。
  • オーバーロードされた仮想関数のデフォルトパラメータを変更してはならない。
  • T t;はデフォルトコンストラクタの呼び出し。 T t();は関数t()の宣言。
  • const宣言の値渡しは無意味
  • コンパイラがコピーコンストラクタ呼び出しを取り除くことのできる状況は以下の2つのみ。
    • 戻り値最適化(RVO)
    • 一時オブジェクト

コンパイラの最適化は期待している程されないということが判明。やっぱり前回のエントリで書いたことは当たっているみたい。

pimplイディオムについても大変勉強になった。そのうち使うことがあったらまた読み返してみよう。