【C++】staticとは?|使い方と用途をわかりやすく解説

サムネイル画像 C++

C++でプログラムを書いていると、「staticって何?」「どんな時に使うの?」と疑問に思ったことはありませんか?

staticは使う場所によって意味が変わるため、最初は混乱しやすいキーワードです。しかし、正しく理解すれば、メモリ効率やコードの安全性を大きく向上させることができます。

この記事を読み終えると、あなたはstaticの使い方をマスターできると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

staticとは?

staticとは、「この変数や関数は特別な扱いをしますよ」と記憶域期間やスコープを制御するためのキーワードです。

C++では、staticは使う場所によって意味が変わります。

  • 関数内のstatic変数: プログラム終了まで値を保持
  • クラスのstaticメンバ: すべてのインスタンスで共有される
  • グローバルなstatic変数・関数: そのファイル内でのみ使用可能

それぞれ詳しく見ていきましょう。

関数内のstatic変数

関数内でstatic変数を宣言すると、その変数は関数が終了しても値を保持し続けます。

▼main.cpp

#include <iostream>

void countCall() {
    static int count = 0; // 初回のみ実行される
    count++;
    std::cout << "Called " << count << " times\n";
}

int main() {
    countCall();
    countCall();
    countCall();
    
    return 0;
}

実行結果

Called 1 times
Called 2 times
Called 3 times

通常の変数なら毎回0に初期化されますが、static変数は値を保持し続けます。

staticを付けない場合と比較してみましょう。

▼main.cpp

#include <iostream>

void countCallNormal() {
    int count = 0; // staticなし
    count++;
    std::cout << "Called " << count << " times\n";
}

int main() {
    countCallNormal();
    countCallNormal();
    countCallNormal();
    
    return 0;
}

実行結果

Called 1 times
Called 1 times
Called 1 times

staticなしでは、毎回0にリセットされてしまいます。

クラスのstaticメンバ変数

クラスでstatic変数を宣言すると、すべてのインスタンスで共有される変数になります。

▼main.cpp

#include <iostream>

class Player {
public:
    static int totalPlayers; // static メンバ変数
    
    Player() {
        totalPlayers++;
    }
};

// static メンバ変数の定義(クラス外で必要)
int Player::totalPlayers = 0;

int main() {
    std::cout << "Total: " << Player::totalPlayers << "\n";
    
    Player p1;
    std::cout << "Total: " << Player::totalPlayers << "\n";
    
    Player p2;
    std::cout << "Total: " << Player::totalPlayers << "\n";
    
    return 0;
}

実行結果

Total: 0
Total: 1
Total: 2

staticメンバ変数は、各インスタンスごとではなく、クラス全体で1つだけ存在します。

重要なポイント

  • staticメンバ変数はクラス外で定義が必要
  • クラス名::変数名でアクセスできる
  • すべてのインスタンスで値を共有する

クラスのstaticメンバ関数

staticメンバ関数は、インスタンスを作らずに呼び出せる関数です。

▼main.cpp

#include <iostream>

class Math {
public:
    static int add(int a, int b) {
        return a + b;
    }
    
    static int multiply(int a, int b) {
        return a * b;
    }
};

int main() {
    // インスタンスを作らずに呼び出せる
    std::cout << "3 + 5 = " << Math::add(3, 5) << "\n";
    std::cout << "3 * 5 = " << Math::multiply(3, 5) << "\n";
    
    return 0;
}

実行結果

3 + 5 = 8
3 * 5 = 15

staticメンバ関数は、非staticメンバ変数にアクセスできないという制限があります。

▼main.cpp

#include <iostream>

class Example {
public:
    int normalVar = 10;
    static int staticVar;
    
    static void showStatic() {
        std::cout << staticVar << "\n";      // OK
        // std::cout << normalVar << "\n";   // エラー!
    }
};

int Example::staticVar = 20;

int main() {
    Example::showStatic();
    return 0;
}

実行結果

20

staticメンバ関数はインスタンスに紐づかないため、通常のメンバ変数にはアクセスできません。

グローバルなstatic変数・関数

ファイルのグローバルスコープでstaticを使うと、その変数や関数はそのファイル内でのみ使用可能になります。

▼sub.cpp

static int secret = 100; // このファイル内でのみ使える

static void privateFunc() {
    // このファイル内でのみ呼び出せる
}

▼main.cpp

#include <iostream>

// extern int secret; // エラー!他のファイルのstatic変数は使えない

int main() {
    // privateFunc(); // エラー!他のファイルのstatic関数は呼べない
    return 0;
}

これにより、ファイル内部の実装を隠蔽できます。

モダンC++では、staticの代わりに無名名前空間(namespace)を使う方が推奨されています。

▼sub.cpp

namespace {
    int secret = 100; // このファイル内でのみ使える
    
    void privateFunc() {
        // このファイル内でのみ呼び出せる
    }
}

【重要】私が実際にstaticで困った体験談

体験談1: 関数内でstatic付け忘れて毎回同じ値だった

大学の課題で「敵を倒した回数をカウントする関数」を作っていた時のことです。以下のようなコードを書きました。

int getEnemyCount() {
    int count = 0; // staticを付け忘れ
    count++;
    return count;
}

何度敵を倒しても「倒した敵: 1体」としか表示されず、30分以上原因がわかりませんでした。

原因は、staticを付け忘れていたこと。関数を呼ぶたびにcountが0に初期化されていました。

int getEnemyCount() {
    static int count = 0; // staticを追加
    count++;
    return count;
}

staticを追加したら正常に動作しました。関数内で値を保持したい時は、必ずstaticを付けましょう。

体験談2: staticメンバ変数の定義を忘れてリンクエラー

ゲーム制作でプレイヤー数を管理するクラスを作った時、以下のようなコードを書きました。

class Player {
public:
    static int totalPlayers;
    Player() { totalPlayers++; }
};

// ここに定義を書き忘れた!

ビルドすると、以下のようなエラーが出ました。

error LNK2001: 外部シンボル "public: static int Player::totalPlayers" は未解決です

原因は、staticメンバ変数はクラス外で定義が必要だったこと。以下のように修正したら動きました。

class Player {
public:
    static int totalPlayers;
    Player() { totalPlayers++; }
};

// クラス外で定義を追加
int Player::totalPlayers = 0;

この経験から、staticメンバ変数は必ずクラス外で定義することを学びました。

static使用時のよくあるエラーと対処法

エラー1: 関数内static変数が毎回リセットされる

症状: カウント変数が毎回0になってしまう
原因: staticを付け忘れている
対処法: 関数内で値を保持したい変数には必ずstaticを付けてください

// NG
void func() {
    int count = 0;
    count++;
}

// OK
void func() {
    static int count = 0;
    count++;
}

エラー2: リンクエラー「外部シンボルは未解決です」

症状: error LNK2001などのリンクエラーが出る
原因: staticメンバ変数をクラス外で定義していない
対処法: クラス外で必ず定義してください

class MyClass {
public:
    static int value; // 宣言
};

// クラス外で定義が必要
int MyClass::value = 0;

エラー3: staticメンバ関数から通常のメンバ変数にアクセスできない

症状: error C2597: 非静的メンバへの参照が正しくありません
原因: staticメンバ関数は非staticメンバ変数にアクセスできない
対処法: アクセスしたい変数もstaticにするか、関数を非staticにしてください

class MyClass {
public:
    int normalVar = 10;
    static int staticVar;
    
    static void staticFunc() {
        // std::cout << normalVar; // エラー!
        std::cout << staticVar;    // OK
    }
};

実践的な使い方

staticのよくある使い方を見てみましょう。

シングルトンパターン

staticメンバを使って、クラスのインスタンスを1つだけに制限できます。

▼main.cpp

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    Singleton() {} // コンストラクタをprivateに
    
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
    
    void showMessage() {
        std::cout << "Singleton instance\n";
    }
};

Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    
    s1->showMessage();
    
    // s1とs2は同じインスタンス
    std::cout << (s1 == s2 ? "Same instance" : "Different instance");
    
    return 0;
}

実行結果

Singleton instance
Same instance

staticを使うべき場面

staticは以下のような場面で使います。

  • 関数内static変数: 関数の呼び出し回数をカウントしたい時
  • クラスのstaticメンバ変数: すべてのインスタンスで共有したいデータがある時
  • クラスのstaticメンバ関数: インスタンスなしで呼び出したいユーティリティ関数
  • グローバルstatic: ファイル内部の実装を隠蔽したい時(モダンC++では無名名前空間推奨)

まとめ

staticの使い方をまとめます。

  • 関数内static変数: 値を保持し続ける
  • クラスのstaticメンバ変数: すべてのインスタンスで共有される
  • クラスのstaticメンバ関数: インスタンスなしで呼び出せる
  • グローバルstatic: そのファイル内でのみ使用可能(モダンC++では無名名前空間推奨)
  • ✅ staticメンバ変数はクラス外で定義が必要
  • 関数内で値を保持したい時は必ずstaticを付ける
  • ✅ staticメンバ関数は非staticメンバ変数にアクセスできない

これでstaticの使い方はバッチリですね!

適切な場面でstaticを活用して、より効率的なC++プログラムを書いてみてください。

ここまで読んでくださり、ありがとうございました。

この記事が皆様の学習に役立てば幸いです。

関連記事