C++で連結リストを作ってみよう.オーソドックスに作ればこんな感じになる.
class List {
protected:
int nakami;
List *next;
public:
....
};
このリストでは,中身(nakami)は整数型だ.しかし,実際にリストに
入れたいものはさまざまだから,中身ごとにクラスを作り直さなけれ
ばならない.こんなときは,継承を使って記述をシンプルにすることが
できる.
class ListBase {
protected:
ListBase *next;
public:
....
};
class IntList : public ListBase {
pritected:
int nakami;
public:
...
};
これで良さそうだが,ちょっと問題が出てくる.例として,
リストの全要素を表示するメンバ関数を作ってみよう.
class IntList {
...
public:
...
void print() {
IntList *p = this;
while (p) {
printf("%d¥n",p->nakami);
p = (IntList*)next;
}
}
};
nextの型はListBase*なので,IntListの中で
nextを使う場合には,常にIntList*にキャストしなければならない.
この程度の例なら抜け道はあるが,本質的にはリストをたどる処理を
するたびにキャストが必要になる.これでは,せっかく継承を使っても,
とても使いやすいとはいえない.また,リストの要素から値を取り出したり,
また値をセットしたりする操作は,派生クラスごとに書き直さなければならない.
こんなときに便利な機能としてテンプレートがある.テンプレートを使って リストを書くと,こんな感じになる.
template <class T>
class List {
protected:
T nakami;
List<T> *next;
public:
List(T& x, List<T>* n) { nakami = x; next = n; }
‾List() { delete n; }
....
};
template<class T>のTはパラメータで,これを実際に使う
時には,List<int>とかList<double>のように,<>
の中に具体的な型が入る.テンプレートを記述しておくと,<>
の中に入れた型ごとに,それぞれのクラスを記述したのと同じ効果を持つ.
したがって,リストの中身ごとにクラスを派生させる必要はなく,
使った時点で自動的にクラスが生成される.なお,テンプレートは
マクロと同じように作用するので,あらかじめコンパイルしておくことは
できない.通常は,ヘッダファイルに記述される.
複数のファイルで,同じテンプレートから同じクラスを派生させたときに, テンプレートから生成されたメンバ関数の記述がどうコンパイルされるかは, コンパイラの実装に依存する.賢いコンパイラの場合,最終的に統合される ファイル全体を見て,おなじクラスは1回しか生成されないようにしてくれる. 賢くないコンパイラの場合,全く同じ関数がファイルごとに生成されるので, できた実行ファイルは無駄が多いものになってしまう.
なお,テンプレートでは,クラス名だけではなく,整数もパラメータに することができる.