2007-09-23

"Effective C++ 第3版" Scott Meyers 著

目の前に積みあがった専門書の中で一冊の本に目が留まった。パラパラっとめくってみると、落ち着いた色合いでなんとなくピート香がする。これはシングルモルト気分で飲めそうだ。
本書は、C++でプログラミングするためのガイドラインを提示してくれる。そこには、一般的なデザインやC++の使い方などが記されている。例えば、テンプレートと継承、public継承とprivate継承、メンバ関数と非メンバ関数、値渡しと参照渡しなど、それぞれどちらを選択するかについてのヒントを与えてくれる。また、奇抜なテクニックなどなく、むしろ基本から抑えられている点は感銘を受ける。哲学風な要素も含まれ、最近のプログラム思想も味あわせてくれる。但し、あくまでも効率的に使うためのアドバイスであり、よく吟味して使わないと味が薄れるであろう。アル中ハイマーには、いい感じに酔える本である。

C++を概観すると、複数言語の連合であると語られる。大まかな領域は4つである。1つはC言語の領域。2つはオブジェクト指向としての領域。3つはテンプレート。4つはSTL。
アル中ハイマーが関わっているのは、2つ目までの領域である。テンプレートは悪酔いのもとであり、STLはコンテナを使うぐらいなものである。
では、せっかくのアドバイスを忘れないうちに、ざっとメモっておこう。

1. プリプロセッサよりもコンパイラを使おう
#defineよりも、const、enum、inlineを薦めている。
#defineで定義したリテラルは、コンパイラが持つシンボル表には登録されないので、コンパイルエラーで混乱することもある。シンボリックデバッガでも同じことになる。
これは、高林哲氏著の「BINARY HACKS」でも、最近のプログラム手法として、#defineはなるべく使わないようにすると記されている。そこでは理由は記されてなかったが、本書では明確に記されている。

2. なるべくconstを使おう
constは、特定のオブジェクトについて「こうしてはいけない」という制約を明確にできる。メンバ関数へのconstには、物理的な不変性と論理的な不変性の意味がある。物理的な不変性は、オブジェクト内部が1ビットも変更しない場合にconstを付けるべきであると主張している。この長所はコンパイラが知らせてくれるのでルール違反もわかる。

3. オブジェクトを使う前の初期化
コンストラクタで初期値を代入しても、オブジェクト生成時に初期化されるわけではないのでオーバーラップした動作を定義していることになる。したがって、メンバ関数の初期化リストを使うと無駄な動作が省ける。

4. コンストラクタ、デストラクタ、コピー代入演算子
コンストラクタ、デストラクタ、コピー代入演算子は、宣言しなくてもコンパイラが自動生成する。もし、コンパイラが自動生成することを禁止するには、対応するメンバ関数をprivate宣言すれば良い。例えば、コピー代入演算子をprivateで宣言しておけば、このオブジェクトはコピーできない。

5. リソース管理
メモリ確保には必ず解放が必要である。よって、new/deleteは一式で使うことになるが、途中で例外処理が発生したりして解放を忘れることもある。この場合、スマートポインタなどのオブジェクトを使うと良い。そして、リソース管理クラスには、リソースへのアクセスする方法を提供しておく。つまり、メモリ解放時は、デストラクタで自動的に破棄させるように仕向ける。

6. 関数と変数の受け渡し
値渡しより、const参照渡しの方が効率的である。
関数にオブジェクトを渡したり、関数からオブジェクトを受け取る場合、関数の仮引数は、実引数のコピーとして初期化され、戻り値はコピーを受け取る。これはC言語から受け継いだもので、おいらの嫌いな性質である。これらのコピーはコピーコンストラクタによって生成されるので、参照渡しの方が効率が良い。但し、組込み型とSTLの反復子、関数オブジェクトには適用されない。これらは通常値渡しが適当である。

7. カプセル化の思想
データをなるべく遠ざけることにより、変更の柔軟性と機能拡張性を増すことがカプセル化の思想である。よって、データの近くにメンバ関数を置くよりも、メンバでもfriendでもない関数を使うと良いという。こうすれば、変更は限られたクライアント以外に影響を与えない。
個人的には、データを扱うメンバ関数は、データの近くに置くのが整理しやすいと考えていたが、カプセル化の主旨からは外れていると指摘されてしまった。この助言も分からなくはないが、その按配も状況に応じて考えたい。

8. 継承とオブジェクト指向
オブジェクト指向で最も重要なルールは、「public継承はis-a 関係を意味する」であると語る。基底クラスに適応できるものは、全て派生クラスにも適用できるようにしなければならない。コンポジションは、has-a関係と実装関係になる。非仮想のメンバ関数を派生クラスで再定義しない。継承された関数のデフォルト引数値を再定義しない。
おいらは、コンポジションを多く使う。というより自然とそうなってしまう。それと、public継承ぐらいしか使わない。酔っ払いには、この2つで充分である。

9. テンプレートのジェネリックプログラム
操作しているオブジェクトに依存しないジェネリックプログラムが脚光を浴びているようだ。例えば、STLのfindやmergeなどがそれである。テンプレートは進化している。もともとは、コンテナのためだったのだろうが、多様な使われ方をしている。ジェネリックプログラムやコンパイル時にコンパイラ内で実行できるメタプログラミングの手法など。もはやコンテナはテンプレートの一部に過ぎない。ますます悪酔いしそうな世界へと広がる。アル中ハイマーはどう転んでもテンプレートプログラマになることはなさそうだ。

0 コメント:

コメントを投稿