コンテンツにスキップ

最終更新:2026-10-13

C++ static 詳解:静的変数と静的メンバ

作者について

WeChat公式アカウント:

C++ static 詳解:静的変数・静的メンバ・静的関数の使い方 | C++那些事

staticキーワードは、異なる対象と組み合わせて使う際に異なる意味を持ちます。staticキーワードは次のように使えます:

静的変数: 関数内の変数、クラス内の変数

静的なクラスのメンバー: クラスのオブジェクトとクラス内の関数

それでは、これらのstaticの使い方を詳しく見ていきましょう:

静的変数

  • 関数内の静的変数

変数をstaticとして宣言すると、プログラムの生存期間中ずっと**領域が割り当てられます。関数を何度呼び出しても、静的変数の領域は**一度しか割り当てられず、前回の呼び出しでの変数の値が次の関数呼び出しへと引き継がれます。これはC / C++や、関数の以前の状態を保存する必要がある他のあらゆるアプリケーションで非常に役立ちます。

#include <iostream> 
#include <string> 
using namespace std; 

void demo() 
{ 
    // static variable 
    static int count = 0; 
    cout << count << " "; 

    // value is updated and 
    // will be carried to next 
    // function calls 
    count++; 
} 

int main() 
{ 
    for (int i=0; i<5; i++)  
        demo(); 
    return 0; 
} 

出力:

0 1 2 3 4 

上のプログラムでは、変数countがstaticとして宣言されているのがわかります。したがって、その値は関数呼び出しをまたいで引き継がれます。関数を呼び出すたびに変数countが初期化されることはありません。

  • クラス内の静的変数

staticとして宣言された変数は一度しか初期化されず、独立した静的ストレージに領域が割り当てられるため、クラス内の静的変数は**オブジェクト間で共有されます**。異なるオブジェクトごとに同じ静的変数の複数のコピーを持つことはできません。この理由により、静的変数はコンストラクタを使って初期化することはできません。

#include<iostream> 
using namespace std; 

class Apple 
{ 
public: 
    static int i; 

    Apple() 
    { 
        // Do nothing 
    }; 
}; 

int main() 
{ 
Apple obj1; 
Apple obj2; 
obj1.i =2; 
obj2.i = 3; 

// prints value of i 
cout << obj1.i<<" "<<obj2.i; 
} 

上のプログラムでは、複数のオブジェクトに対して静的変数iの複数のコピーを作ろうとしているのがわかります。しかしそれは起こりませんでした。したがって、クラス内の静的変数は、次のようにクラスの外でクラス名とスコープ解決演算子を使って明示的に初期化すべきです:

#include<iostream> 
using namespace std; 

class Apple 
{ 
public: 
    static int i; 

    Apple() 
    { 
        // Do nothing 
    }; 
}; 

int Apple::i = 1; 

int main() 
{ 
    Apple obj; 
    // prints value of i 
    cout << obj.i; 
} 

出力:

1

静的メンバー

  • クラスオブジェクトをstaticにする

変数と同じように、オブジェクトもstaticとして宣言すると、プログラムの生存期間が終わるまでスコープを持ちます。

次のプログラムでは、オブジェクトは非静的です。

#include<iostream> 
using namespace std; 

class Apple 
{ 
    int i; 
    public: 
        Apple() 
        { 
            i = 0; 
            cout << "Inside Constructor\n"; 
        } 
        ~Apple() 
        { 
            cout << "Inside Destructor\n"; 
        } 
}; 

int main() 
{ 
    int x = 0; 
    if (x==0) 
    { 
        Apple obj; 
    } 
    cout << "End of main\n"; 
} 

出力:

Inside Constructor
Inside Destructor
End of main

上のプログラムでは、オブジェクトはifブロック内で非静的として宣言されています。したがって、変数のスコープはifブロック内だけです。そのため、オブジェクトが生成されるときにコンストラクタが呼ばれ、ifブロックの制御が抜けるのと同時にデストラクタが呼ばれます。オブジェクトのスコープが、宣言されたifブロック内に限られるからです。

オブジェクトを静的に宣言すると、出力がどのように変わるか見てみましょう。

#include<iostream> 
using namespace std; 

class Apple 
{ 
    int i; 
    public: 
        Apple() 
        { 
            i = 0; 
            cout << "Inside Constructor\n"; 
        } 
        ~Apple() 
        { 
            cout << "Inside Destructor\n"; 
        } 
}; 

int main() 
{ 
    int x = 0; 
    if (x==0) 
    { 
        static Apple obj; 
    } 
    cout << "End of main\n"; 
} 

出力:

Inside Constructor
End of main
Inside Destructor

出力の変化がはっきりとわかるでしょう。今度は、mainが終わった後にデストラクタが呼ばれます。これは、静的オブジェクトのスコープがプログラムの生存期間全体に及ぶためです。

  • クラス内の静的関数

クラス内の静的データメンバーや静的変数と同じように、静的メンバー関数もクラスのオブジェクトに依存しません。静的メンバー関数をオブジェクトと'.'を使って呼び出すことは許されていますが、クラス名とスコープ解決演算子を使って静的メンバーを呼び出すことが推奨されます。

静的メンバー関数は、静的データメンバーか他の静的メンバー関数にしかアクセスできず、クラスの非静的データメンバーやメンバー関数にはアクセスできません。

#include<iostream> 
using namespace std; 

class Apple 
{ 
    public: 
        // static member function 
        static void printMsg() 
        {
            cout<<"Welcome to Apple!"; 
        }
}; 

// main function 
int main() 
{ 
    // invoking a static member function 
    Apple::printMsg(); 
} 

出力:

Welcome to Apple!

アクセス範囲の限定

staticには、アクセス範囲を限定する役割(匿名名前空間に類似)もあります。issueより:https://github.com/Light-City/CPlusPlusThings/issues/142

// source1.cpp
extern void sayHello();
const char* msg = "Hello World!\n";
int main()
{
    sayHello();
    return 0;
}

// source2.cpp
#include <cstdio>
extern char* msg;
void sayHello()
{
    printf("%s", msg);
}

g++は上の2つのコードファイルを正常にコンパイルしてHello World!を出力できますが、source1.cppのmsgにstaticを付けると、undefined reference to 'msg'というコンパイルエラーが発生します:

// source1.cpp
extern void sayHello();
static const char* msg = "Hello World!\n";
int main()
{
    sayHello();
    return 0;
}

staticの内部リンケージ(アクセス範囲の限定)

issue #142 の指摘に感謝します:staticがグローバル変数/関数を修飾するとき、**アクセス範囲を限定する**役割も果たします。つまり、リンケージを外部リンケージから内部リンケージに変え、その効果は匿名名前空間に似ています:

  • staticでないグローバル変数:他の翻訳単位は extern 宣言を通してアクセスできます;
  • staticなグローバル変数/関数:現在の翻訳単位からのみ見え、extern 宣言でもリンクできません(undefined reference)。

現代のC++では、「このファイル内でのみ見える」ことを表すのに**匿名名前空間**を使うことがより推奨されます(型に対しても同様に有効です)が、staticのこの層の意味を理解することは、古いコードを読むときに非常に重要です。

// file1.cpp
static int hidden = 42;          // 内部链接:其他文件 extern 不可见
int visible = 1;                 // 外部链接:其他文件可 extern 访问

namespace {                      // 匿名命名空间:等价且更现代的写法
    int also_hidden = 7;
}

よくある質問

static グローバルと通常グローバルの違いは?

static グローバルは内部結合で、その翻訳単位内でのみ可視です。通常グローバルはファイル横断で可視で、シンボル衝突の原因になります。

static ローカル変数はいつ初期化される?

定義を最初に通過したときに 1 回だけ。C++11 以降その初期化はスレッドセーフで、寿命はプログラム終了までです。

静的メンバ関数は非静的メンバにアクセスできる?

できません。this ポインタがないため、静的メンバのみアクセスできます。

関連トピック

コメント