第29章 静かなるメンバ2

 前回で静的メンバ変数というものがどんなものかは分かったかもしれませんが、例えばどういうときに使えばいいのでしょうか? 今回はそこに迫ってみたいと思います。


 では、今回の要点です。


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


 今回は静的メンバ変数についてもうちょっと考えてみたいと思います。

 静的メンバ変数を使えば、例えばどんなことができるでしょうか? 例えば、クラスの実体の数を数えておくことなどができるでしょう。

// SMember2.cpp
#include <iostream.h>

class CNumOf
{
private:
    static int m_nNumOf;  // CNumOf の全実体数

public:
    CNumOf()   { m_nNumOf++; }
    ~CNumOf()  { m_nNumOf--; }
    void Disp(){ cout << m_nNumOf << endl; }
};

int CNum::m_nNumOf;

int main()
{
    CNumOf a, b, c;
    a.Disp();
    return 0;
}

 しかし、ここでふと疑問が浮かびます。m_nNumOf は初期化されているのでしょうか?

 と、疑問が浮かんだら、とにかく実験してみましょう。

実行結果
3

 3 と表示されました。ということは、どうやら0で初期化されているのだと推測できます。

 実は、これはたまたま0で初期化されてたのではなく、0で初期化されるようになっているから0で初期化されていたのです。

 これは何かと似ている気がしませんが? そうです。静的変数の特徴と似ていますね。静的変数は、特に指定しなければ0で初期化されるのでしたね。「静的」と付いていることからわかるように、静的メンバ変数も静的変数なのです

 ということは、静的メンバ変数を好きな値で初期化するには

int CNumOf::m_nNumOf = 10;

とでき、この初期化が起こるのはプログラムの一番最初だということが、芋づる的に分かります。

 静的内部変数を思い出してみましょう。どこで何回関数を呼んでも、静的内部変数は同じメモリ領域を使うものでした。この特徴は静的メンバにも言え、どこで何個クラスの実体を作っても同じメモリ領域を使うわけです。


 では、静的メンバ変数を持ったクラスから派生するとどうなるのでしょうか? 派生クラスでは別の変数が用意されるのでしょうか? それとも、派生クラスでも同じメモリ領域を使うのでしょうか?

 答は後者です。派生クラスでも同じメモリ領域を使います。静的メンバ変数は派生してすら1つしかできないのです。

 このことを利用すると、そのクラスとその派生クラスの全実体を列挙することができます

プログラム1
// Enum.h
#define ENUM_MAX_SIZE  32

class CEnum
{
private:
    static CEnum* m_pThis[ENUM_MAX_SIZE];  // 実体へのポインタ
    static int    m_nNumOf;                // 実体数

protected:
    int m_num;  // 何かの数値

public:
    CEnum(int num);
    virtual ~CEnum();

    virtual void Disp();  // 何かを表示
    void DispAll();       // 全実体の何かを表示
};

class CEnumChild : public CEnum
{
private:
    double m_num2;  // 何かの数値2

public:
    CEnumChild(int num, double num2);
    virtual void Disp();  // 何かを表示
};
プログラム2
// Enum.cpp
#include <iostream.h>
#include "Enum.h"

// これは 0 (NULL) で初期化されます
CEnum* CEnum::m_pThis[ENUM_MAX_SIZE];  // 実体へのポインタ
int    CEnum::m_nNumOf;                // 実体数

CEnum::CEnum(int num)
{
    int i;

    // 手抜きで可変長にはしません
    if(m_nNumOf == ENUM_MAX_SIZE)
        return;

    // NULL な場所を探す
    for(i = 0; i < ENUM_MAX_SIZE; i++)
    {
        if(m_pThis[i] == NULL)
        {
            m_pThis[i] = this;
            break;
        }
    }
    m_nNumOf++;

    m_num = num;
}

CEnum::~CEnum()
{
    int i;

    // 自分自身を表から削除する
    for(i = 0; i < ENUM_MAX_SIZE; i++)
    {
        if(m_pThis[i] == this)
        {
            m_pThis[i] = NULL;
            break;
        }
    }
}

// 何かを表示
void CEnum::Disp()
{
    cout << "CEnum だよ : " << m_num << endl;
}

// 全実体の何かを表示
void CEnum::DispAll()
{
    int i;

    // 全実体の Disp を呼ぶ
    for(i = 0; i < ENUM_MAX_SIZE; i++)
    {
        if(m_pThis[i] != NULL)
            m_pThis[i]->Disp();
    }
}

CEnumChild::CEnumChild(int num, double num2) : CEnum(num)
{
    m_num2 = num2;
}

// 何かを表示
void CEnumChild::Disp()
{
    cout << "CEnumChild だよ : " << m_num
         << " : " << m_num2 << endl;
}
プログラム3
// SMember3.cpp
#include <iostream.h>
#include "Enum.h"

void Viss(const int num)
{
    cout << "Viss : No." << num << endl;
}

CEnum      a(1);
CEnumChild b(2, 2.5);

int main()
{
    a.DispAll();
    Viss(1);

    CEnum      c(3);
    CEnumChild d(4, 4.5);

    a.DispAll();
    Viss(2);

    {
        CEnumChild e(5, 5.5);

        a.DispAll();
        Viss(3);
    }
    a.DispAll();

    return 0;
}
実行結果
CEnum だよ : 1
CEnumChild だよ : 2 : 2.5
Viss : No.1
CEnum だよ : 1
CEnumChild だよ : 2 : 2.5
CEnum だよ : 3
CEnumChild だよ : 4 : 4.5
Viss : No.2
CEnum だよ : 1
CEnumChild だよ : 2 : 2.5
CEnum だよ : 3
CEnumChild だよ : 4 : 4.5
CEnumChild だよ : 5 : 5.5
Viss : No.3
CEnum だよ : 1
CEnumChild だよ : 2 : 2.5
CEnum だよ : 3
CEnumChild だよ : 4 : 4.5

 全実体の Disp を呼ぶことができました。静的メンバ変数を使うとこんなこともできてしまうのです。


 それでは今回の要点です。


 次回も静的メンバについてです。でも今度は変数じゃなくて...。


第28章 静かなるメンバ | 第30章 静かなるメンバ3

Last update was done on 2000.10.22

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