★ファイル分割(スコープ3)★


これまで「ローカル」「グローバル」というスコープ(適用範囲)について説明しました。

最後は「ファイル」というスコープについて説明します。

まずは次のプログラムを準備して、実行してみましょう。

  main.h

  main.cpp

  sub.h

  sub.cpp

↑の4つのファイルをプロジェクトに追加してください。


<sample program 165-01>


/* main.h */

#pragma once

#include "sub.h"


/* main.cpp */

#include "main.h"

#include <stdio.h>

int value;

void ShowValue(void);

int main(void)
{
    value = 24;

    ShowMessage();

    ShowValue();

    return 0;
}

void ShowValue(void)
{
    printf("value = %d\n", value);
}


/* sub.h */

#pragma once

void ShowMessage(void);


/* sub.cpp */

#include "sub.h"

#include <stdio.h>

void ShowMessage(void)
{
    printf("Start!\n");
}

<実行結果>

Start!
value = 24
続行するには何かキーを押してください・・・

今回のグローバル変数valueは main.cpp の中でしか使わないため、maincpp の中で宣言しています。

関数のプロトタイプ宣言に当たる、externがヘッダファイルに無いため、sub.cpp では変数valueを使うことは出来ません。

このように、あるcppファイルに限定して使うグローバル変数を使う時があります。

1つのファイルに範囲を限定しているだけでバグが発生しても、このファイルだけを確認すれば解決しそうです。


しかし、この書き方には穴があるのです。

sub.cpp を↓のように書き換えて実行してみてください。



/* sub.cpp */

#include "sub.h"

#include <stdio.h>

extern int value;

void ShowMessage(void)
{
    printf("Start!\n");

    value *= 2;
}

<実行結果>

Start!
value = 48
続行するには何かキーを押してください・・・

変数valueの中身が2倍されていますね。

sub.cpp で(勝手に)extern 宣言を書き、グローバル変数valueの中身を書き換えています。

このように、他のファイルで(勝手に)extern 宣言されると、折角ファイルに限定していたグローバル変数がどこでも使えるようになってしまいます。

これに対抗する手段を説明します。

main.cpp を↓のように書き換えてください。



/* main.cpp */

#include "main.h"

#include <stdio.h>

static int value;

void ShowValue(void);

int main(void)
{
    value = 24;

    ShowMessage();

    ShowValue();

    return 0;
}

void ShowValue(void)
{
    printf("value = %d\n", value);
}


main.cpp の変数宣言の前に static というキーワードを付けました。

static については関数のところ(146.htmへのリンク)で説明しましたが、今回は別の使い方になります。

ビルドしてみてください。

  sub.obj : error LNK2001: 外部シンボル ""int value" (?value@@3HA)" は未解決です。

sub.cpp に書かれている、

extern int value;

が元でビルドエラーになっています。

グローバル変数に static キーワードを付けることによって、その変数はファイル内でしか使えない変数になるのです。

これを「ファイルスコープ」と言います。

適用範囲をファイルに限定するということですね。


適用範囲は狭ければ狭いほど、バグに強いプログラムになります。

関係ないファイルなどから情報を隠蔽することは非常に重要なことです。

「グローバル」より「ファイル」、「ファイル」より「ローカル」スコープといった具合です。


これでお終い!ではなく、実は関数も同じ問題を抱えています。

sub.cpp を次のように書き換えてみてください。



/* sub.cpp */

#include "sub.h"

#include <stdio.h>

void ShowValue(void);

void ShowMessage(void)
{
    printf("Start!\n");

    ShowValue();
}

<実行結果>

Start!
value = 24
value = 24
続行するには何かキーを押してください・・・

関数についても(勝手に)プロトタイプ宣言を書くことで、他のファイルの関数を呼び出すことが可能です。

通常、他のファイルで使って欲しい関数は、ヘッダファイルにプロトタイプ宣言を書きます。

必要であれば、それをインクルードすれば良いのです。

逆にヘッダファイルにプロトタイプ宣言が無い関数は、他のファイルから使って欲しくない関数ということです。

しかし、(勝手に)プロトタイプ宣言を書くことで使えてしまうのです。

これを防ぐのも static キーワードです。

main.cpp を↓のように書き換えてください。



/* main.cpp */

#include "main.h"

#include <stdio.h>

static int value;

static void ShowValue(void);

int main(void)
{
    value = 24;

    ShowMessage();

    ShowValue();

    return 0;
}

void ShowValue(void)
{
    printf("value = %d\n", value);
}


他のファイルで使って欲しくない関数のプロトタイプ宣言に static を付けます。

この状態でビルドすると、エラーになりました。

  sub.obj : error LNK2019: 未解決の外部シンボル "void __cdecl ShowValue(void)" (?ShowValue@@YAXXZ) が関数 "void __cdecl ShowMessage(void)" (?ShowMessage@@YAXXZ) で参照されました。

無理やり、他のファイルでプロトタイプ宣言を書くことが出来なくなった訳です。

このように、グローバル変数やグローバル関数の適用範囲を狭くすることで、少しでもバグの発生が減らせるようにします。

よく分からない内は、引数で渡すようにしてください。

必要なデータだけ、引数で渡すのが無難だと思います。


次へ

戻る

目次へ