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