第30章 静かなるメンバ3

 前回まではメンバ変数を静的にしてきました。では、メンバ関数を静的にするとどうなるのでしょうか? 今回はそういうことをやりたいと思います。


 では、今回の要点です。


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


 前回の SMember2.cpp をもう一度見てみましょう。

// 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;
}

 ここで、a.Disp(); というのがありますが、これは別に b.Disp(); であっても、c.Disp(); であっても問題ありません。

 それどころか、よくよく考えてみれば実体が存在しないときでも使えそうです。先ず、静的メンバ変数は実体がなくても常に存在しています。そして、静的メンバ変数はプログラムの初めに初期化されているのですから、コンストラクタが呼ばれていなくても m_nNumOf を参照することに問題はありません。

 静的メンバ変数は外部変数でも(やるべきではありませんが)一応置き換えられるのでしたね。置き換えた場合、このようにできると思います。

#include <iostream.h>

int g_nNumOf;  // CNumOf の全実体数

class CNumOf
{
public:
    CNumOf() { g_nNumOf++; }
    ~CNumOf(){ g_nNumOf--; }
};

void Disp(){ cout << g_nNumOf << endl; }

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

 そうです。普通の関数を呼べばいいですね。

 こういうことをメンバ関数を使って実現するにはどうすればいいのでしょうか?


 そういうときは、メンバ関数に static を付ければいいのです。

 static の付いたメンバ関数、つまり静的メンバ関数は、実体がなくても呼べるメンバ関数です。

 そのかわり、静的メンバ関数は this を持ちません。つまり、静的メンバ以外のメンバを操作できません

 静的メンバ関数を呼ぶには、従来の a.Disp(); でも構いませんし、

CNumOf::Disp();

という風にすることもできます。第21章でも出てきた文法ですね。

 以上を踏まえてプログラムを作ってみましょう。

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

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

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

int CNum::m_nNumOf;

int main()
{
    CNumOf::Disp();
    CNumOf a, b, c;
    CNumOf::Disp();
    return 0;
}
実行結果
0
3

 実体が作られる前は 0 、実体を3つ作った後は 3 と表示されました。期待通りの動作ですね。


 普通のメンバ関数と静的メンバ関数との違いは、構造体と普通の関数を使って考えてみるとよく分かると思います。

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

struct SNum
{
    int num;
};

int g_nNum;  // SNum の全実体数

// コンストラクタ
void InitSNum(SNum* psnum, int num)
{
    psnum->num = num;
    g_nNum++;
}

// デストラクタ
void DestSNum(SNum* psnum)
{
    g_nNum--;
}

// 普通のメンバ変数の値を表示
void DispSNum(SNum* psnum)
{
    cout << psnum->num << endl;
}

// 静的メンバ変数に相当する値を表示
void Disp()
{
    cout << m_nNum << endl;
}

int main()
{
    Disp();

    SNumOf a, b;
    InitSNum(&a, 100);
    InitSNum(&b, 300);

    Disp();
    DispSNum(&a);
    DispSNum(&b);

    DestSNum(&a);
    DestSNum(&b);
    return 0;
}
実行結果
0
2
100
300

 メンバ関数は呼ぶときに使った実体のアドレスを渡すのでした。しかし、静的メンバ関数では実体のアドレスを渡しません。実体がなくても呼べますしね。これが普通のメンバ関数と静的メンバ関数の大きな違いです。

 呼んだときに使った実体のアドレスが渡されないのですから、静的メンバ関数内では静的メンバしか扱えません。上の SMember4.cpp の Disp 内で、SNum の num や DispSNum が使えないのと同じことなのです。

 このように、静的メンバの挙動が分からなくなったら、一旦メンバ関数を使わなかったらどうなるかを考えるといいでしょう。


 前回の全実体の Disp を呼ぶという関数 DispAll も静的メンバ変数しか利用してないので、静的メンバ関数にしてしまうといいでしょう。

 静的メンバ関数をクラスの宣言外で実装するときは、静的メンバ変数と同じく static を付けません(もちろんプロトタイプには付けます)。注意しましょう。

 静的メンバ関数内で Disp を呼んでいますが、それは静的メンバ変数に実体のアドレスを保存しているからです。呼んだときの実体がどれかということは全く関係無しに、同じ動作を行います。そして、呼んだときの実体がどれかということで分岐することもできません。


 では、今回の要点です。


 ようやくクラスの基本が終わりました。次からはテンプレートというものについて話したいと思います。それでは。


第29章 静かなるメンバ3 | 第31章 冶金工場

Last update was done on 2000.10.29

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