「C++再考」の訳注集3
第5章で言いたいこと
こんにちは。ようやく、「C++再考」の本番です。
この章を読んで、みなさんはどう思うでしょう。代理クラスの目的がなんだかわかるでしょうか?自然に中級にさしかかっている人なら、この辺から、この本はいろいろな悩みをスカッとさせてくれるはずです。
しかし、ちょっと背伸びをしている人には、なんだか目的不明のことを繰り返しているように見えるかもしれません。そう感じる人のための訳注です。
具体的な話の方がわかりやすいでしょう。まず、C++入門24のNekoのプログラムを見てください。簡単に整理すると、
1.猫クラス(Neko)を定義
2.猫の派生クラスのサラリー猫、ボーナス付きサラリー猫、資産家猫(SalaryNeko、SalaryNekoWithBonus、SisankaNeko)を定義
3.ユーザに5猫(ごにんと読む)のデータを入力させる。その際、サラリー猫かボーナス付きサラリー猫か資産家猫かをユーザに選ばせて、入力させる。
4.その5猫に年収(と名前)を言わせる。
というものでした。
ユーザが入力するたびにnewでNekoの派生クラスを作り、それをNekoへのポインタの配列(要素が五つのもの)へ格納していき、最後にNekoの仮想関数として定義したGetNensyuで年収を計算し出力しているのでした。(名前を出力させるところここでは省略します。)骨組みは、下のような感じです。
//猫Nekoの定義
//サラリー猫などの定義
void main()
{
Neko* cat[5];
//猫の種類を聞き、答えに応じて
//cat[i] = new SalaryNeko(...)、cat[i] = new SlaryNekoWithBonus(...)、
//cat[i] = new SisankaNeko(...)をする。もちろん、iは0から4まで動かす。
for(i=0;i<5;i++){
cout<<"年収:"<<cat[i]->GetNensyu()<<"万円"<<endl;
}
//後始末
for(i=0;i<5;i++) delete cat[i];
}
このプログラムのポイントは、ユーザが使うまでどの猫が登録されるかわからない、にも関わらず、仮想関数のおかげでちゃんと動く!ということです。この簡単な練習プログラムは、予想外に評判が良かったものです。このしくみを使ってプログラムを書いてみました、、、なんていうメールもいただいて、私はちょっとうれしかったです。
しかし、実は、長いプログラムになっていくとこの仕掛けだけでは、間違い(バグ)が多くなるのです。これは、自分で長いプログラムを書いてみないとわからないのですが、、、。(もちろん、上のプログラムは悪いというのではありません。単に入門用ということです。そして、C++では「入門用のコード」で間に合うところはそれでもよいのです。この点はC++再考の大著者さんも強調しています。それはそれで、案外大事なことなので、誤解の無いように。)
問題は、ポインタを生で使っているというところです。後始末のところで、deleteをしていますが、いつもこんな風に、最後に気持ち良く後始末できるわけではありません。プログラムが複雑になってくると、途中でいらない猫はdeleteし、また別の猫をnewし、また一部deleteし、、、とやっていくことになります。すると、deleteのヤリソコナイやヤリスギをしてしまうものなのです。それはあんたが悪い、、、と思いますか?でも、人間なんてそんなものです。
ここでたとえば「代理クラス」を作ると、こういう問題が解決するのです。代理クラス(Surrogate)を使うとプログラムは次のようになります。
//猫Nekoの定義(ちょっと変更)
//サラリー猫などの定義(ちょっと変更)
//代理猫SurrogateNekoの定義
void main()
{
SurrogateNeko cat[5]; //ポインタではないことに注意
//猫の種類を聞き、答えに応じて
//cat[i] = SalaryNeko(...)、cat[i] = SlaryNekoWithBonus(...)、
//cat[i] = SisankaNeko(...)をする。もちろん、iは0から4まで動かす。
//newなんか使っていない!!!
for(i=0;i<5;i++){
cout<<"年収:"<<cat[i].GetNensyu()<<"万円"<<endl;
}
//後始末なし つまりdeleteなんかない
}
代理クラスを作る手間は、初心者には大変に感じられるかもしれません。そんなに面倒な思いをしたわりには、上のプログラムはたいして改善されていないように見えるでしょうか?しかし、プログラマがnewやdeleteを何度もしないでよい、というのは大変な長所です。「安全対策」というものは、常に最初は面倒なものです。しかし、一度やっておきさえすれば、かなりの利得になるはずです。そして、C++のよいところは、一度やれば、後はそれを利用できるというところです。
なお、ポインタを使わない利点は、安全性のほかに、代入演算子を書くことで代入をコントロールできるということなどもあります。(まあ、これも間違った代入をさけるということで、安全性の問題でもありますが、、、。)
「C++再考」では乗り物クラスを例に説明しています。