質問
他のファイルからincludeされないソースファイルに定義したグローバル変数は (未定義動作を除いた)いかなる場合も同じ値を共有するか
cpp
1//test.h 2#pragma once 3 4//このクラスをいろいろなヘッダ・ソースファイルから呼び出す 5struct Test { 6 Test(); 7 void f(); 8}; 9
cpp
1//test.cpp 2//このファイルは他のファイルから#include "test.cpp"されない プロジェクトに含めるだけ 3 4#include "test.h" 5 6struct A{ 7 int a{}; 8 /* 9 いろいろな変数 10 . 11 . 12 . 13 */ 14}; 15 16//グローバル変数 17A GlobalA{}; 18 19 20//////////////////////////// 21 22Test::Test(){ 23 GlobalA.a = 100; 24} 25 26void Test::f(){ 27 //どこから呼び出してGlobalA.aは100であるかどうか 28 std::cout << GlobalA.a <<std::endl; 29} 30 31
環境
C++20
VisualStudio2019
質問の背景
コンストラクタTest::TestでA::aに100を入れたはずなのに,Test::f関数でA::aが0になっている現象に出くわしています
・ちゃんとA::aは同じアドレスを指しています
・ポインタに変えたらA::aに100が入っており期待通りに動作します
とりあえず簡単な例で自分の理解があっているのか確認したいです
その他
何か質問や追記依頼があれば追記欄にお願いします
よろしくお願いいたします
追記
原因がわかった気がします
グローバル変数GlobalAの初期化より前にコンストラクタTest::Test
をよびだす場合に問題が発生します
以下の順番で呼び出されています
- Test::Test
- Test::Testを抜ける
- A::A
- GlobalAのコンストラクタを抜ける(ここでGlobalAは初期化されるのでTest::Testで書き換えたの内容は消える)
- fを呼ぶがTest::Testで書きこんだ内容は消えデフォルト初期化でaには0が入っている
しかし気になるのがユニークポインタにしたときです
ユニークポインタであればしっかりと動作します
cpp
1//test.cpp 2 3#include "test.h" 4 5struct A{ 6 int a{}; 7 /* 8 いろいろな変数 9 . 10 . 11 . 12 */ 13}; 14 15//グローバル変数 16std::unique_ptr<A> GlobalA = nullptr; 17 18 19//////////////////////////// 20 21Test::Test(){ 22 GlobalA = std::make_unique<A>(); 23 GlobalA->a = 100; 24} 25 26void Test::f(){ 27 //この例では100が入っている 28 std::cout << GlobalA->a <<std::endl; 29} 30
この例の場合は次のような順番です
- Test::Test
- Test::TestでGlobalAをstd::make_unique<A>()
- GlobalAのコンストラクタを抜ける(ここでGlobalAは
std::unique_ptr
のstd::nullptr_t
を引数にとるコンストラクタで初期化されるのでTest::Testで書き換えたの内容は消えるはず) - fを呼ぶがTest::Testで書きこんだ内容は消えデフォルト初期化でaには0が入っている
と思いきや消えていない...
> コンストラクタTest::TestでA::aに100を入れたはずなのに,Test::f関数でA::aが0になっている現象に出くわしています
そうなるコードを示していただけませんか?
> とりあえず簡単な例で自分の理解があっているのか確認したいです
簡単な例に直し過ぎて、問題が発現するコードが消えてしまっているのではないでしょうか。あくまで提示のコードで、そのような問題が発生するかどうかだけを問いたいのでしょうか。
main()関数に入る前の初期化の順番ってなにか決まっているんでしたっけ。
つまり、
それぞれ静的寿命を持つGlobalAとTestのインスタンスの初期化の順番について何らかの期待をすることは正しいんですっけ?
複数のglobal変数があるとき、コンストラクタの動く順序は規定されないはず。
なのでこれも”(未定義動作を除いた)いかなる場合”にはあたらないかと。
「グローバル変数GlobalAの初期化より前にコンストラクタTest::Testをよびだす場合に問題が発生します」と追記あるため、Testクラスもまたグローバル変数として初期化されるようですね。
FYI: 異なるソースコードに配置されるグローバル変数初期化順については https://in-neuro.hatenablog.com/entry/2020/12/12/001244 が詳しいと思います。回避方法もあわせて紹介されています。
回答3件
あなたの回答
tips
プレビュー