Array クラスを作る (size フィールド編)

Java では、配列の要素数をフィールド length を通じて参照することができます。

//合計の取得
int sum(int[] anData){

    int nSum =0;

    int i;
    for (i=0; i<anData.length; ++i){
        nSum +=anData[i];
    } //i

    return nSum;
}

この length は (おそらく) final int、即ち、変更不能な public フィールドとして定義されており、これに対する代入操作を行おうとすると、コンパイルエラーとなります。 ところで、この仕組みは Java が「配列のサイズは変更できない」という仕様を有するが故に可能なものであるということに読者諸兄はお気づきでしょうか。 もし配列のサイズが初期化後に変更可能であるとすれば、lengthfinal 修飾することはできません。 そうなると、length に対する代入操作を禁止する手立てはなくなってしまうのです。

一方、C++ ならば、値の変更が可能かつ、外部からの変更が不能なメンバ変数 (を模したインターフェイス) を定義することが可能です。 このエントリでは、このテクニックを使い、これまでのエントリで作成してきた Array クラステンプレート (参照) に要素数読み取り専用のメンバ変数 size を定義します。

一般的な解決策

読み取り専用メンバ変数を使う前に、より一般的に使用される手法、即ち、データ読み取り用メンバ取得関数 (getter/reader method) を検討してみましょう。

//要素数の取得
template<typename T> int Array<T>::Size() const {
    return m_nSize;
}

一般に、メンバ変数の値を読み取るだけの手段を提供するのであれば、これがもっとも自然で簡単なやり方です。 しかし、C++ では引数の数が 0個であっても、関数呼び出しの () を省略することはできないので、

//合計の取得
int sum(const Array<int> anData){

    int nSum =0;

    int i;
    for (i=0; i<anData.Size(); ++i){
        nSum +=anData[i];
    } //i

    return nSum;
}

どうにも不恰好な (?) コードに。 ここをなんとかして、Java のように anData.size とスマートに書きたいわけです。

const 参照をメンバ変数にする

さて、いよいよ読み取り専用メンバ変数を定義する方法について解説しましょう。 とは言っても、そのやり方はとても簡単。 size という名前の const 参照を public なメンバ変数として追加するだけです。

//配列クラステンプレート
template <typename T> class Array {
public:

    const int& size;

そして、コンストラクタの初期化リストを使用して、そのターゲットを指定します。 ターゲットとなるのはもちろん、メンバ変数 m_nSize です。

//コンストラクタ
template <typename T> Array<T>::Array()
: size(m_nSize), m_nSize(0), m_lpa(NULL) {
}

//コンストラクタ
template <typename T> Array<T>::Array(const Array<T> a)
: size(m_nSize) {

    if (a.m_nSize){

        int nSize =a.m_nSize;
        T*  lpa   =new T [nSize];

        int i;
        for (i=0; i<nSize; ++i){
            lpa[i] =a.m_lpa[i];
        } //i

        m_nSize =nSize;
        m_lpa   =lpa;
    }
    else {
        m_nSize =0;
        m_lpa   =NULL;
    }
}

上記コードにはデフォルトおよびコピーコンストラクタのみを挙げましたが、他のコンストラクタについても同様に、初期化リスト内に size(m_nSize) を加えてください。

これでようやく、念願の読み取り専用メンバ変数 (を模したインターフェイス) ができました。

//合計の取得
int sum(const Array<int> anData){

    int nSum =0;

    int i;
    for (i=0; i<anData.size; ++i){
        nSum +=anData[i];
    } //i

    return nSum;
}

配列のサイズが変更されても (つまり、メンバ変数 m_nSize の値が変わっっても)、この size によって参照される値は要素数と一致します。 もちろん、これに代入を行おうとするとコンパイルエラーとなります。

成田