【ワレコのC++】グローバル変数・定数を複数ファイルで共有する【実践的】

この記事は約8分で読めます。
スポンサーリンク

さて、先ほど以下の記事を執筆した。

【ワレコのC言語】グローバル変数・定数を複数ファイルで共有する【実践的】
ワテの場合、プログラミングには最近ではマイクロソフト社のVisual StudioのC#を使う事が多いのだが、昔はC言語を良く使っていた。その後、C++も使い始めて、最近ではC/C++言語系で開発する場合にはC++を使う事が多い。しかしなが

上の記事では、C言語でプログラム開発をする場合に、複数ファイル間でグローバル変数を宣言して初期値を定義する手法の中でも、ワテが良く使っているオーソドックスな手法を紹介した。

 

一方、当記事ではそれのC++版を紹介したい。

ワテの場合、最近のC++は難解過ぎて完全には使いこなせていないが、まあ、ある程度は使いこなせる。

まずは、C++の複数ソースファイル間でグローバル変数やグローバル定数を共有する手法を紹介したい。

では本題に入ろう。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
スポンサーリンク
スポンサーリンク

C++でmain.cppとsub1.cppファイルでグローバル変数・定数の共有

何をしたいのかと言うと、下図にその概略を図解してみた。

 

図 C++でmain.cppとsub1.cppファイルでグローバル変数・定数の共有方法(ワテ流)

上図で青い矢印には深い意味はない。単に赤枠の説明文が関係しているソースコードを指しているだけだ。

C++でプログラム開発を行う場合には、通常は複数のソースコードに分けて作成する。

多い場合だと100ファイル以上になる事もある。

さて、ここではその実例として二つのソースファイル

main.cpp
sub1.cpp

と一つのヘッダーファイル

Header.h

があり、その両者でグローバル変数と定数を以下のように宣言して初期化もしたい。

int g_i = 1;
const int c_i=1000;

また、sub1.cppソースファイルの中だけで有効なグローバル変数と定数を以下のように宣言して初期化もしたい。

int s_i_sub1 = 2;
const int c_i_sub1=200;

それを図にしたものが上図だ。

物凄くヘンテコな変数のネーミングかも知れないが、そこは気にせず読み進めて頂きたい。

要するに、g_i と c_i はプログラム全体で共有されるグローバル変数(グローバルスコープ)なので、先頭に g_  を付けてみた。

一方、s_i_sub1 と c_i_sub1 はファイルsub1.cpp内の関数だけで共有されるスタティック(静的)なグローバル変数(ファイルスコープ)なので先頭に s_ を付けてみた。

const定数の場合には先頭に c_ を付けてみた。まあワテ流なので、世間の標準ではないと思う。

 

では、その三つのファイルを紹介しょう。

Header.hの中身

以下のように定義している。

#pragma once
  
// グローバル変数宣言と初期化 
__declspec(selectany) int g_i = 1;
__declspec(selectany) int g_iarr[] = { 1, 2, 3, 4 };
__declspec(selectany) double g_darr[] = { 1.0, 2.0, 3.0, 4.0 };
 
 
// グローバル定数宣言と初期化
//x  extern const int c_i;
//x  const __declspec(selectany) int c_i = 1000;
//ok extern const __declspec(selectany) int c_i = 1000;
const extern __declspec(selectany) int c_i = 1000; // constとexternが必要
//ok extern  __declspec(selectany)const int c_i = 1000;

冒頭で紹介したC言語版では、#ifdef ~ #else ~ #endif の手法とGLOBAL_DEFINEと言う名前のマクロ定義の有無で、externをグローバル変数の冒頭に付ける/付けないを制御すると言う小細工的手法で有った。

C++の場合にも、その手法を用いても良いのだが、Visual Studioの場合にはもっとシンプルな手法が使えるので、ワテの場合にはそれを良く使っている。

具体的に言うと、 __declspec(selectany) と言う構文だ。

上のHeader.hに示すように記述しておくだけで、後はグローバル変数を使いたいファイルでこのヘッダーファイルをインクルードするだけで良いのだ。

初期値を与えている部分もそれぞれのソースファイルで展開されるが、特にエラーする事もなく、正しく期待通り初期化出来る。

グローバル変数の場合とグローバル定数の場合とで、多少記述方法が異なるようだ。

グローバル定数の場合には、上のコードに示すように、何通りかの記述方法がある。まあどれでも良いみたいなので好きな奴を選べば良いだろう。

 

MSDNでselectanyの使い方を見たい人はここに説明がある。

selectany

まあ、英語なのでワテには良く分からん。

 

main.cppの中身

#include "Header.h";
#include <stdio.h>
 
int func1(int* pi);
 
 
int main()
{
    int i = 2;
 
    int result = func1(&i);
    printf("result=%d\n", result);    // 1492
 
    result = func1(&i);
    printf("result=%d\n", result);    // 1192
 
 
    int iend = sizeof(g_darr) / sizeof(g_darr[0]); // =4
    for (i = 0; i < iend; i++) {
        printf("g_darr[%d]=%20.15f\n", i, g_darr[i]);
    }
 
    return 0;
}
 

このmain.cppに関しては、先に説明したmain.cと全く同じである。

ただし、main.cでやっていた冒頭の #define GLOBAL_DEFINE 文はもう必要ないので削除している。

sub1.cppの中身

こちらはsub1.cと完全に同じだ。

 
#include "Header.h"
 
static int s_i_sub1 = 2;
static const int c_i_sub1 = 200;
 
int func1(int* pi)
{
    static int s_i_func1 = 3;
    const int c_i_func1 = 30;
 
    int result
        = c_i*g_i
        + c_i_sub1*s_i_sub1
        + c_i_func1* s_i_func1
        + *pi
        ;
 
    s_i_func1 -= 3;
    s_i_sub1--;
    *pi = -8;
 
    return result;
}

ファイルの拡張子 .cpp  .h  .hpp などに付いて

Visual Studio2017のC++空プロジェクトを選んで上の三つのファイルを保存する。

拡張子 .cpp で保管するとそれはC++の文法で記述されていると解釈されてC++コンパイラーに掛けられる。

なお、C++のヘッダーファイルの拡張子は .hpp や .h のどちらも使われるが、Visual Studioの場合にはどちらでも良い。特にどちらを使えば良いなどの決まりは無いみたいだ。

gccなどの他のコンパイラーはどうなのかな?

その辺りはワテは詳しくない。

なお、C++でテンプレート関数を使う場合には、通常は宣言と実装をヘッダーファイルに書く必要があるが、そう言う場合には拡張子 .hpp を好む人もいるようだ。

ワテもそうする。

でも、ワテの場合には、.hppにはテンプレート関数宣言を書いて、テンプレート関数の実装はソースファイル .cpp に書く方が好きなので、そうしている。

ただし、その辺りのやり方は良く忘れるので、また機会があれば別の記事にまとめたいと思う。

実行結果

実行してみよう。

result=1492
result=1192
g_darr[0]= 1.000000000000000
g_darr[1]= 2.000000000000000
g_darr[2]= 3.000000000000000
g_darr[3]= 4.000000000000000
続行するには何かキーを押してください . . .

デバッグ無しで開始(CTRL+F5)を押すと実行出来る。

この辺りは、先に説明したC言語版と全く同じ結果になる。

まとめ

この記事では、C++でプログラミングをする場合に、複数ソースファイル間でグローバル変数やグローバル定数を共有して利用する手法を紹介した。

ワテの場合には、最近では専らVisual Studio 2017での開発が多い。

今回紹介した __declspec(selectany) の構文を使う手法はVisual Studioでは使えるが、他のコンパイラーgccなどではどうなのかは未確認だ。

機会が有れば調査したい。

 

C++の場合には、今回紹介したC++専用の手法でも良いし、あるいは、先に紹介したC言語版のGLOBAL_DEFINE方式を用いても良い。

まあ好き好きなのであるが、ワテの場合には、最近ではC言語では無くてC++を良く使う。

あるいは殆どC言語の文法しか使わない場合でも、ファイル拡張子を .cpp にしておくとC++特有の各種便利機能を使う事が可能だ。

なので、ワテの場合にはCでもC++でも取り敢えず拡張子は .cpp にするようにしている。

スポンサーリンク
ワテ推薦のプログラミングスクールで学ぶ
コメント募集

この記事に関して何か質問とか補足など有りましたら、このページ下部にあるコメント欄からお知らせ下さい。

C/C++Visual Studio
スポンサーリンク
warekoをフォローする
スポンサーリンク
われこ われこ

コメント