static

静的メンバ変数

クラスはインスタンスを生成して使用するのが基本です。
インスタンスはいくつでも作成でき、それぞれのインスタンスでメンバ変数の値は異なります。

しかし、すべてのインスタンスで共通の値を持つメンバ変数を作ることができます。
これを静的メンバ変数と言います。


#include <iostream>

class TestClass
{
    int num;

    //静的メンバ変数
    static int sNum;
    
public:
    TestClass(int n = 0) { num = n; }

    int get() { return num; }

    int getStatic() { return sNum; }
    void setStatic(int n) { sNum = n; }
};

//静的メンバ変数の定義
int TestClass::sNum = 0;

int main()
{
    TestClass tc1(10);
    TestClass tc2(20);

    tc1.setStatic(50);

    std::cout << tc1.get() << std::endl; //10
    std::cout << tc2.get() << std::endl; //20

    std::cout << tc1.getStatic() << std::endl; //50
    std::cout << tc2.getStatic() << std::endl; //50

    std::cin.get();
}

インスタンスtc1を通して静的メンバ変数sNumの値を書き換えると、インスタンスtc2の方も同じ値になっていることがわかります。

静的メンバ変数はインスタンスに依存せず、クラスに依存する変数となります。
例えば全てのインスタンスで共通して使用する文字列を格納したり、現在のインスタンスの総数を保存しておくなどの使い方が考えられます。
いくつインスタンスを生成しても静的メンバ変数はひとつしか作られないので、メモリの節約にもなります。

静的メンバ変数はクラス内で宣言しますが、実際の定義はクラス外で行います。
その際には「クラス名::静的メンバ変数名」という形で、スコープ解決演算子を使用します。

ただし静的メンバ変数がconst整数型である場合はクラス内で宣言と初期化を同時に行うことができます。


class TestClass
{
    static const int sNum = 10;
};

静的メンバ変数をpublic領域に置いている場合、クラス外から直接アクセスが可能となります。
その場合にはやはり「クラス名::静的メンバ変数名」という形で記述します。


#include <iostream>

class TestClass
{
public:
    //静的メンバ変数
    static int sNum;
};

//静的メンバ変数の定義
int TestClass::sNum = 0;

int main()
{
    TestClass tc1, tc2;
    tc1.sNum = 1;
    tc2.sNum = 2;

    std::cout << tc1.sNum << std::endl; //2
    std::cout << tc2.sNum << std::endl; //2
    std::cout << TestClass::sNum << std::endl; //2

    std::cin.get();
}

静的メンバ関数

クラスはインスタンスを生成してから使うのが基本ですが、インスタンスを生成せずにクラス内の関数を直接呼び出すことができます。
これを静的メンバ関数と言います。


#include <iostream>
#include <string>
#include <time.h>

class MyDateTime
{
    static tm getLocalTime()
    {
        time_t t;
        time(&t);
        tm local;
        localtime_s(&local, &t);
        return local;
    }

public:
    enum TimeName { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND };

    static int getDateTime(TimeName tn)
    {
        tm local = getLocalTime();
        switch (tn)
        {
        case TimeName::YEAR:
            return local.tm_year + 1900;
        case TimeName::MONTH:
            return local.tm_mon + 1;
        case TimeName::DAY:
            return local.tm_mday;
        case TimeName::HOUR:
            return local.tm_hour;
        case TimeName::MINUTE:
            return local.tm_min;
        case TimeName::SECOND:
            return local.tm_sec;
        default:
            return 0;
        }
    }

    static std::string getDateTimeString()
    {
        tm local = getLocalTime();
        char tmp[20];
        snprintf(tmp, 20, "%04d/%02d/%02d %02d:%02d:%02d",
            local.tm_year + 1900, local.tm_mon + 1, local.tm_mday,
            local.tm_hour, local.tm_min, local.tm_sec);
        return tmp;
    }

    static char *getGreeting()
    {
        int hour = getDateTime(HOUR);
        if (hour >= 5 && hour < 11)
            return "おはよう。";
        if ((hour >= 18 && hour < 24)
            || (hour >= 0 && hour < 5))
            return "こんばんは。";
        return "こんにちは。";
    }
};

int main()
{
    std::cout << MyDateTime::getDateTime(MyDateTime::YEAR) << std::endl;
    std::cout << MyDateTime::getDateTimeString() << std::endl;
    std::cout << MyDateTime::getGreeting() << std::endl;
    std::cin.get();
}

MyDateTime内のメンバ関数はすべてstaticで定義しています。
静的メンバ関数は、クラスのインスタンスを生成せずに直接呼び出して使用することができます。
呼び出し方は静的メンバ変数の時と同じく「クラス名::静的メンバ関数名」という形で行います。

staticメンバ関数からのアクセス制限

静的メンバ関数から静的でないメンバ変数/関数にアクセスすることはできません。
静的でないメンバは、インスタンスが生成されて初めて実体が存在します。
静的メンバはインスタンスに依存せず直接呼び出せるため、アクセス可能な非静的メンバ(インスタンス)が存在しないかもしれません。
また、インスタンスが複数存在する場合はどのインスタンスのメンバにアクセスすればよいかが分からなくなってしまいます。

静的でないメンバ関数から静的メンバ変数/関数にアクセスする場合はこのような問題はないため、アクセス可能です。


class TestClass
{
public:
    static int sNum;
    int num;

    static void sPrint()
    {
        //OK
        std::cout << sNum << std::endl;

        //NG
        std::cout << num << std::endl;
    }

    void print()
    {
        //OK
        std::cout << sNum << std::endl;

        //OK
        std::cout << num << std::endl;
    }
};