第5章 隠せ!

 初めに、public: をつけないとメンバが利用できないと言いました。しかし、これは厳密には正しくないのです。では、正しくはどうなのでしょうか? 今回はそういうお話です。


 では、今回の要点です。


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


 さて、今回は新しいクラスを作りたいと思います。int 型100要素の配列を扱うクラスです。早速骨子をみてみましょう。

#include <memory.h>

class CIntArray
{
public:
    int m_anum[100];

    CIntArray();
};

// コンストラクタ
CIntArray::CIntArray()
{
    // memset はバイト単位でデータを埋める関数です
    memset(m_anum, 0, sizeof m_anum);
}

 m_anum というのが配列です。コンストラクタでは memset という関数が呼ばれています。これは第1部第67章でも1回出たのですが、バイト単位で同じデータを埋めていくという関数です。つまり、ここでは配列全体を0で初期化しているわけです。

 このようにすれば、初めに変な値が入っているということを防げますね。

 しかし配列を使うときには、この問題以外にももう1つ大きな問題があります。それは0〜99番以外の要素に書き込んだ時に危険であるということです。この点をクラスを使ってどうにかできないものでしょうか?

 実は、これはそれほど難しいことではありません。メンバ変数 m_anum を関数を通して利用してやればいいのです。引数にはインデックスをとります。このインデックスをチェックして、これが0〜99になかったときは特別な処理をするようにすればいいのです。

 その関数 Get, Set を追加すると、次のようになります。(コンストラクタの実装部は省略します。)

#include <iostream.h>
#include <process.h>

#define ELEM(array)   (sizeof (array) / sizeof *(array))

class CIntArray
{
public:
    int m_anum[100];

    CIntArray();
    int Get(const int index);
    void Set(const int index, const int value);
    void CheckIndex(const int index);
};

// メンバへのアクセス関数
int CIntArray::Get(const int index)
{
    CheckIndex(index);
    return m_anum[index];
}

void CIntArray::Set(const int index, const int value)
{
    CheckIndex(index);
    m_anum[index] = value;
}

// インデックスのチェック
void CIntArray::CheckIndex(const int index)
{
    if((unsigned int)index < ELEM(m_anum))
        return;

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

 変なインデックスを使おうとするとエラーメッセージを表示し、プログラムを強制終了する関数 exit を呼ぶようにしました。このようにすれば、Get, Set を使いさえすれば変なことにはならないわけです。

 しかし、しかしです。m_anum を直接使ってしまえばこの工夫も全く意味がないではありませんか! 「使わないようにして下さい」と言われても、もしかしたら使ってしまうかもしれません。いっそのこと、使えないようにはできないのでしょうか?


 ここで、第1章を思い出して下さい。ここでこういうことを言いました。

「public:(公開された)というものを書いて初めて、メンバにアクセスできるようになる」

 実は、この「アクセスできるようになる」という表現は不正確で、正しくは「外部からアクセスできるようになる」となります。「外部」というのは、「そのクラスのメンバ関数以外」ということです。

 つまり、public: がなくても、メンバ関数内ならそのメンバを使うことができるのです。初めに public: の話を聞いて変だと思った人は正解で、実はこういうことだったのです。

 「メンバ」と言っているように、このことはメンバ変数、メンバ関数、両方に言えることです。「外部」から使えないメンバ関数を作ることもできます。丁度上の CheckIndex 関数はそうした方がよさそうですね。

 しかし、順番を調えるために public: を書いた後にも外部に公開したくないメンバが出てくるかもしれません。そういうときは private: というものを書きます。private:(私有の)というものを書けば、それ以降のメンバはそのクラスのメンバ関数からしか利用できなくなります。

 これら public: と private: のことをアクセス指定子と呼びます。間違いをなくすためにも、アクセス指定子は必要だと思ったところにはこまめに書いておいた方がいいでしょう。あとからアクセス指定子を増やしたいときに便利です。

 では、最終的に CIntArray クラスは次のようになりました。(実装部は省略します。)

class CIntArray
{
    // メンバ変数
private:
    int m_anum[100];  // 配列

    // コンストラクタ
public:
    CIntArray();

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

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

 これでやっと安全に配列が扱えるようになりました。

 このように、メンバ変数を「外部」から隠し関数を通して扱うようにすることによって、メンバ変数の値を保証したり、その扱いを安全にしたりすることができます。アクセス指定子はそのためにとても重要な役割を果たすのです。

 この応用として、インデックスが0から始まらない配列を扱うクラスなども作ることができます。一度挑戦してみてはどうでしょうか?


 では、今回の要点を public:


 それでは、次回まで。


第4章 引数付きの構築 | 第6章 デストラクタ

Last update was done on 2000.8.6

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