質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

1652閲覧

ソースファイルに定義されたグローバル変数は必ず共有されるか

oio

総合スコア1

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/09/26 00:00

編集2021/09/26 01:00

質問

他のファイルから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_ptrstd::nullptr_tを引数にとるコンストラクタで初期化されるのでTest::Testで書き換えたの内容は消えるはず)
  • fを呼ぶがTest::Testで書きこんだ内容は消えデフォルト初期化でaには0が入っている

と思いきや消えていない...

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

episteme

2021/09/26 00:11

> コンストラクタTest::TestでA::aに100を入れたはずなのに,Test::f関数でA::aが0になっている現象に出くわしています そうなるコードを示していただけませんか?
dodox86

2021/09/26 00:19

> とりあえず簡単な例で自分の理解があっているのか確認したいです 簡単な例に直し過ぎて、問題が発現するコードが消えてしまっているのではないでしょうか。あくまで提示のコードで、そのような問題が発生するかどうかだけを問いたいのでしょうか。
thkana

2021/09/26 01:53

main()関数に入る前の初期化の順番ってなにか決まっているんでしたっけ。 つまり、 それぞれ静的寿命を持つGlobalAとTestのインスタンスの初期化の順番について何らかの期待をすることは正しいんですっけ?
episteme

2021/09/26 03:46 編集

複数のglobal変数があるとき、コンストラクタの動く順序は規定されないはず。 なのでこれも”(未定義動作を除いた)いかなる場合”にはあたらないかと。
yohhoy

2021/09/27 08:09 編集

「グローバル変数GlobalAの初期化より前にコンストラクタTest::Testをよびだす場合に問題が発生します」と追記あるため、Testクラスもまたグローバル変数として初期化されるようですね。 FYI: 異なるソースコードに配置されるグローバル変数初期化順については https://in-neuro.hatenablog.com/entry/2020/12/12/001244 が詳しいと思います。回避方法もあわせて紹介されています。
guest

回答3

0

コンストラクタTest::Testが動かなければA::aは初期値のままなので

C++

1#include "test.h" 2 3int main() { 4 Test* p = nullptr; 5 p->f(); 6}

コレ↑ならおそらく0が出力されるでしょう。
が、そもそもpがnullptrであるときに、p->f() したら未定義動作でしょうから、
"(未定義動作を除いた)いかなる場合も"にはあたらないかな。

投稿2021/09/26 00:27

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

VisualStudioなどのデバッガで動作を追いかけてみればどうでしょう
どの段階でその値が変わるのかを見ることができると思います

投稿2021/09/26 00:06

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

解決法としてはmain関数内で初期化する

cpp

1 2Hoge hoge{};//このコンストラクタでTestのコンストラクタが呼ばれるとき今回の問題が起きる 3 4

main関数内に書くか初期化を遅らすとよい

cpp

1 2std::optional<Hoge> hoge = std::nullopt; 3//とか 4//std::unique_ptr<Hoge> hoge = nullptr; 5 6int main(){ 7 //Hoge hoge{};//ここにかく 8 //hoge = Hoge{}; 9 //hoge = std::make_unique<Hoge>(); 10 11} 12

投稿2021/10/08 11:03

oio

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問