2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

静的変数に関する知識

Last updated at Posted at 2022-10-15

何についての記事か

c++の静的変数:静的記憶域期間(static storage duration)を持つ変数についての知識について記載しています。
静的記憶域期間(static storage duration)を持つ変数とはプログラム実行開始時にメモリに値が格納され、プログラムの実行が終了するまでそこにあり続ける変数のことです。

まとめ

  • 静的変数の種類
    • グローバル変数
    • 静的メンバ変数
    • 静的ローカル変数(関数内static変数)
  • 静的変数の初期化仕様
    • 前提
      • 静的変数の初期化はmain()前に行われる
    • 初期化種類
      • 静的初期化(コンパイル時に完了)
        • コンパイル時に値が決まる場合はプログラム開始時にその値が書き込まれる
        • 値が決まらない場合は0が書き込まれる
      • 動的初期化
        • コンストラクタが呼ばれコンパイル時には決まらなかった値が設定される
    • 初期化順
      • 静的初期化→動的初期化
      • 異なる翻訳単位の静的変数は順序不定
      • 同じ翻訳単位の静的変数は宣言順に初期化
      • templateクラスの静的メンバ変数(明示的特殊化なし)の場合はすべて不定

静的変数の種類

  • グローバル変数
//main.cpp
static int a = 10;//これ
extern int b;//これ
int main(){
}

//b.cpp
int b = 10;
  • 静的メンバ変数
    静的メンバ変数は個々のインスタンスが持つことはなく、クラスSampleの全てのインスタンスで共通となる。
struct Sample {
	void CountUp() {
		num++;
		std::cout << "num: " << num << std::endl;
	}
	static int num;
};


int main(){
    Sample instance1;
	instance1.CountUp();//num==1

	Sample instance2;
	instance2.CountUp();;//num==2
}
  • 静的ローカル変数(関数内static変数)
    関数が初めてcallされた際に初期化される。初期化順を規定したい場合に使用したりする。
void f()
{
    static int num = 0;//これ
    num += 1;
    return ;
}

int main(){
    f();//num == 1
    f();//num == 2
}

静的変数の仕様を知らないと何がまずいか

「Static Initialization Order Fiasco」と呼ばれる問題に出会います。
静的変数の初期化順序が不定であるために起きる問題の総称で、"Fiasco"は「
(野心的な企てがこっけいな結果で終わるような)大失敗」という訳だそうです。
main()開始前に○○してやろうと思ったときは一度、静的変数の仕様に注意を払う必要がありそうです。

Static Initialization Order Fiascoで紹介されている例は以下です。
yのコンストラクタでX::xsにpush_backしているが、X::xsの初期化はyのコンストラクタの前と後どちらでしょうか。push_backした後に初期化されたら不具合です。

struct X
{
    static std::vector<int> xs;
};

struct Y
{
    Y() {X::xs.push_back(42);}
};
static Y y;
std::vector<int> X::xs{};


ここからは初期化の仕様についてです。
初期化には2種類あり、静的初期化→動的初期化の順で実行されます。

  • 初期化種類
    • 静的初期化(コンパイル時に完了)
      • コンパイル時に値が決まる場合はプログラム開始時にその値が書き込まれる
      • 値が決まらない場合は0が書き込まれる
    • 動的初期化
      • 静的初期化の後に実行される
      • コンストラクタが呼ばれコンパイル時には決まらなかった値が設定される
//Test.h
struct Test {

	Test(int n) : num(n) {

	}
	int num;
};

//Test.cpp
Test staticA(10);


//main.cpp
extern Test staticA;//コンパイル時に0で初期化

int staticB = staticA.num;//静的初期化の値を使用してしまっている

int main() {
	std::cout << "staticB: " << staticB << std::endl;//staticB==0 静的初期化の値を使用してしまっている
	std::cout << "staticA.num: " << staticA.num << std::endl;;//動的初期化(コンストラクタ)で10がSetされている
}


  • 初期化順
    • 異なる翻訳単位の静的変数は順序不定
    • 同じ翻訳単位の静的変数は宣言順に初期化
    • templateクラスの静的メンバ変数(明示的特殊化なし)の場合はすべて不定

参考

コンパイル時初期化を強制する"constinit"
Static Initialization Order Fiasco

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?