ネームスペース(名前空間)
ネームスペースとはグローバル変数や関数、クラスに対して所属先(有効範囲)を設定し、
名前の衝突の回避やデータ(定数やクラス)の所属先を明確にするための機能です。
■名前の衝突
グローバル変数や関数やクラスは1つのプログラム内で完全一致の名前は
認められておらずエラーになります。
問題例:
Enemy.h
#ifndef ENEMY_H_
#define ENEMY_H_
static const int Max = 10; // 敵最大数
#endif
Shot.h
#ifndef SHOT_H_
#define SHOT_H_
static const int Max = 100; // 弾最大数
#endif
上記のコードをコンパイルした場合、以下のエラーのような多重定義エラーが出ます。
error C2370: 'Max' : 再定義 ; 異なるストレージ クラスです。
Cではこのエラーの回避方法は変数名を変更するしかありませんでした。
修正例:
Enemy.h
#ifndef ENEMY_H_
#define ENEMY_H_
static const int EnemyMax = 10; // 敵最大数
#endif
Shot.h
#ifndef SHOT_H_
#define SHOT_H_
static const int ShotMax = 100; // 弾最大数
#endif
C++では変数名の変更による修正ではなくnamespaceによる修正の方法があります。
■書式
●宣言
書式例:
namespace namescpaeの名前
{
// グローバルやクラスなどの定義
}
具体例:
Enemy.h
#ifndef ENEMY_H_
#define ENEMY_H_
namespace Enemy
{
static const int Max = 10; // 敵最大数
}
#endif
Shot.h
#ifndef SHOT_H_
#define SHOT_H_
namespace Shot
{
static const int Max = 100; // 弾最大数
}
#endif
●使用時(namespaceのスコープ外)
書式例:
namespace名::所属しているデータ
具体例:
#include "Enemy.h"
#include "Shot.h"
void main(void)
{
printf("敵の最大数 = %d\n", Enemy::Max);
printf("弾の最大数 = %d\n", Shot::Max);
}
出力結果:
敵の最大数 = 10
弾の最大数 = 100
上記の具体例のようにnamespaceを使用することで
Enemy.hとShot.hのMaxがEnemyとShotに所属することになったので
コンパイラが別の変数と認識してエラーもなく実行可能となりました。
使用時の項目名で「namespaceのスコープ外」と書いているのは
namespaceのスコープ内ならば「namespace名::」は省略可能だからです。
// Enemy.h
namespace Enemy
{
static const int EnemyMax = 10; // 敵最大数
void PrintMax(void)
{
printf("敵の最大数 = %d\n", Max);
}
}
■階層化(入れ子)
namespaceは階層化(入れ子)にすることができます。
●書式
書式:
namespace namespace名その1
{
namespace namespace名その2
{
// 定数、クラス宣言
}
}
具体例:
// ファイル
namespace File
{
テクスチャ
namespace Texture
{
static const int Max = 100;
}
// サウンド
namespace Sound
{
static const int Max = 20;
}
static const int Max = Texture::Max + Sound::Max;
}
void main(void)
{
printf("ファイル最大数 = %d\n", File::Max);
}
●注意
入れ子にすることで細かい分別ができるようになりますが、
あまり細かすぎると、定数使用時のコードが長くなるので、
やりすぎないように注意しなければいけません。
問題コード:
namespace Test1
{
namespace Test2
{
namespace Test3
{
namespace Test4
{
namespace Test5
{
static int Max = 100;
}
}
}
}
}
void main(void)
{
// 入れ子になりすぎて読みづらい
printf("%d\n", Test1::Test2::Test3::Test4::Test5::Max);
}
■using
usingはnamespaceをスコープ内に追加する機能を持っています。
●書式
書式例:
using namespace namespace名;
具体例:
// Enemy.h
namespace Enemy
{
static const int Max = 10; // 敵最大数
void PrintMax(void)
{
printf("敵の最大数 = %d\n", Max);
}
}
#include "Enemy.h"
// namespace Enemyをスコープに追加
using namespace Enemy;
void main(void)
{
PrintMax();
}
出力結果:
敵の最大数 = 10
上記の具体例のようにusingを使用した場合、指定したnamespaceの所属している
データを「namespace名::」を指定しなくても使えるようになります。
●注意点
usingはnamespaceの所属情報を指定しなくていいので便利な一面がありますが、
名前の衝突問題が再燃しますので、使用しすぎないように注意しなければいけません。
また、usingはヘッダで使用してはいけません。
usingの影響が他のcppでも出てしまい、namespaceを使用している意味がなくなります。