第37章 クラステンプレート

 今回もテンプレートなのですが、今回は関数ではなくクラスの話をします。その名もクラステンプレートです。


 では、今回の要点です。


 では、いってみましょう。


 さて、みなさん CIntArray を思いだして下さい。このクラスは、動的に int 型の配列を確保するためのクラスでした。

 では、例えば char 型の配列を使いたければどうすればいいのでしょうか? 普通は CIntArray のソースを使い回したくなると思います。int の部分を char に変えれた CCharArray を作ればそれで終わりなわけです。

 しかし、このシリーズに新しい機能を加えたいとします。すると、CIntArray と CCharArray の両方を書き換えなければなりません。CCharPtrArray 、CDoubleArray などいろいろ増やしていけば、もう収拾がつきません。

 こういうときはテンプレートの出番です。テンプレートは「処理は同じだが型だけが違っているものを1つにまとめることができる」というものでした。まさに今やりたいことそのものです。

 問題は「テンプレートはクラスもサポートしているのか?」ということですが、クラスもテンプレートにできます。テンプレートにしたクラスのことをクラステンプレートと呼びます。


 では、CIntArray をテンプレートにして CArray クラスを作ってみましょう。

 とりあえず文法のことは置いておいて、次に CArray クラスを示します。これを見ればどんな文法なのかは推測できると思います。文法の詳しい解説は次回に回して、今回はじっくりソースをなめ回してみて下さい。

プログラム1
// Array.h
#ifndef __ARRAY_H__INCLUDED__
#define __ARRAY_H__INCLUDED__

#include <iostream.h>
#include <stdlib.h>
#include <memory.h>

template <typename TYPE>
class CArray
{
    // メンバ変数
private:
    TYPE* m_pnum;    // 動的配列
    int   m_nNumOf;  // 配列の要素数

    // コンストラクタ・デストラクタ
public:
    explicit CArray(const int nNumOf);
    CArray(const CArray <TYPE> & rother);  // コピーコンストラクタ
    virtual ~CArray();

    // メンバへのアクセス関数
public:
    TYPE Get(const int index) const;
    void Set(const int index, const TYPE value);

    // メンバの参照
    TYPE& operator [](unsigned int index);
    TYPE& operator [](int index);
    TYPE operator [](unsigned int index) const;
    TYPE operator [](int index) const;

    operator const TYPE*() const;  // 配列の直接参照

    // インデックスのチェック
private:
    void CheckIndex(const int index) const;

    // その他の情報の取得
public:
    bool IsValid() const;  // m_pnum の値が有効かどうか
    int NumOf() const;     // 配列の要素数
    int SizeOf() const;    // 配列のサイズ

    // コピー
public:
    bool Copy(const CArray <TYPE> & rother);  // 配列のコピー
    CArray <TYPE> & operator =(const CArray <TYPE> & rother);
                                              // = 演算子のオーバーロード

    // 諸関数
private:
    void Init();     // メンバの初期化
    void Release();  // メモリの解放
};

// コピーコンストラクタ
template <typename TYPE>
CArray <TYPE> ::CArray(const CArray <TYPE> & rother)
{
    Init();
    Copy(rother);
}

// デストラクタ
template <typename TYPE>
CArray <TYPE> ::~CArray()
{
    Release();
}

// メンバへのアクセス関数
template <typename TYPE>
TYPE CArray <TYPE> ::Get(const int index) const
{
    CheckIndex(index);
    return m_pnum[index];
}

template <typename TYPE>
void CArray <TYPE> ::Set(const int index, const TYPE value)
{
    CheckIndex(index);
    m_pnum[index] = value;
}

// インデックスのチェック
template <typename TYPE>
void CArray <TYPE> ::CheckIndex(const int index) const
{
    if((unsigned int)index < (unsigned int)m_nNumOf)
        return;

    cout << "インデックスが不正です!" << endl
         << "値 : " << index << endl;
    exit(1);
}

// メンバの初期化
template <typename TYPE>
void CArray <TYPE> ::Init()
{
    m_pnum   = NULL;
    m_nNumOf = 0;
}

// メモリの解放
// 配列が確保されているときだけメモリを解放します
// 解放した後はメンバを初期化します
template <typename TYPE>
void CArray <TYPE> ::Release()
{
    if(IsValid() == true)
    {
        delete [] m_pnum;
        Init();
    }
}

#endif // #ifnde __ARRAY_H__INCLUDED__
プログラム2
// TmplCls1.cpp
#include <stdio.h>
#include "Array.h"

int main()
{
    static const char str1[] = "Schroedingerの猫";
    CArray <char> str2(sizeof str1);
    CArray <char> str3(0);
    int i;

    for(i = 0; i < str2.NumOf(); i++)
        str2[i] = str1[i];
    cout << str2 << endl;

    str3 = str2;
    str3[sizeof str1 - 3] = (char)('犬' >> 8);
    str3[sizeof str1 - 2] = (char)('犬' & 0xFF);
    printf("%s\n", (const char*)str3);

    return 0;
}
実行結果
Schroedingerの猫
Schroedingerの犬

 実は関数の実装をいくつもわざと抜かしているので、それらを追加してみて下さい。追加しないと TempCls.cpp はコンパイルできません。抜かした関数は、コンストラクタ、m_pnum の有効確認関数、要素数・サイズの取得関数、コピー関数、[ ] 演算子4つ、キャスト演算子、= 演算子です。CIntArray のソースを改造して...どうです? できそうですか?

 また、「テンプレートは全てヘッダファイルに書く」ことにも注意しましょう。


 今回はこれで終わりです。では、要点です。


 次回までに CArray をいろいろいじって遊んでみて下さい。それでは。


第36章 特注の鋳型 | 第38章 クラステンプレート2

Last update was done on 2000.12.6

この講座の著作権はロベールが保有しています