ホームに戻る
出典 :
関連 :
目次 :
名前空間とは
名前(シンボル)の有効範囲を定めるもの。
名前空間を用いることで、それぞれの名前の「所属」を明示できるため、コードを構造的に管理することができる。
特にライブラリとして公開する場合など、独自の名前空間を設定しておくことで、他のプログラムとの名前の競合を避けることができる。
基本的な例
// 名前空間 A
namespace A
{
void f()
{
std::cout << "A" << std::endl;
}
void hoge()
{
// (同じ名前空間の) f() を呼び出す
f();
}
} //< 名前空間 A ここまで
// 名前空間 B
namespace B
{
void f()
{
std::cout << "B" << std::endl;
}
void hoge()
{
// 名前空間 A の f() を呼び出す
A::f();
}
} //< 名前空間 B ここまで
void main()
{
A::f(); //< 名前空間 A の f()
B::f(); //< 名前空間 B の f()
A::hoge(); //< 名前空間 A の hoge()
B::hoge(); //< 名前空間 B の hoge()
}
実行結果 :
A //< 名前空間 A の f()
B //< 名前空間 B の f()
A //< 名前空間 A の hoge()
A //< 名前空間 B の hoge()
ここでは、「A」と「B」の名前空間を宣言し、それぞれで f() 、hoge() 関数を定義している。
これらの関数は所属する名前空間が異なるため別物として扱われ、共存が可能である。
同一の名前空間にあるシンボルは特別な手順を踏まずに呼び出すことができるが、
異なる名前空間にあるシンボルは :: (スコープ解決演算子)を用いて、「どの名前空間に属するか」を明示する必要がある。
名前空間の階層化
// 名前空間 A1
namespace A1
{
// 名前空間 A1::A2
namespace A2
{
// 名前空間 A1::A2::A3
namespace A3
{
void f()
{
:
}
} //< 名前空間 A3 ここまで
} //< 名前空間 A2 ここまで
} //< 名前空間 A1 ここまで
void main()
{
// 名前解決
A1::A2::A3::f();
}
名前空間の中に、さらに名前空間を作成することも可能である。
名前空間の取り込み
using キーワードを用いることで、特定の名前空間をファイル中で用いることができる。
#include <iostream>
// std 名前空間を使用
using namespace std;
void main()
{
// std:: を省略できる
cout << "AAAAA" << endl;
}
名前空間を取り込むことで名前解決は簡略化できるものの、取り込んだ名前空間にあるシンボルと同じ名前を使用できないという弊害がある。
特に std 名前空間は非常に多くのシンボルが定義されており、安易に取り込むと名前の衝突(名前汚染)を招く可能性が高い。
(また、標準ライブラリが拡充された際には新たに名前衝突が発生する可能性もある。)
このため、上記のようなグローバルスコープへの名前空間取り込みは、通常避けられるべきである。
但し以下のような手法で、名前汚染を避けることが可能である。
適用範囲を限定して取り込む
#include <iostream>
void DoSomething1()
{
// この関数にのみ std を取り込む
using namespace std;
// std::cout 、std::endl は
// std:: を省略できる
cout << "AAAAA" << endl;
}
void DoSomething2()
{
// std:: を省略できない
std::cout << "AAAAA" << std::endl;
}
上記の例では、DoSomething1() 関数でのみ using を行っている。
このため、関数内では名前空間を省略できるが、using を行っていない他の関数では省略ができない。
シンボルを限定して取り込む
#include <iostream>
// std::cout 、std::endl のみを取り込み
using std::cout;
using std::endl;
// 自作の count() 関数
int count()
{
return 0;
}
void main()
{
int arr[] = { 0, 1, 0, 2 };
int ret1, ret2;
// count() (自作)の呼び出し
ret1 = count();
// std::count() の呼び出し
// std:: は省略できない
ret2 = std::count(arr, arr + sizeof(arr), 0);
// std::cout 、std::endl は
// std:: を省略できる
cout << "AAAAA" << endl;
}
上記の例では、std 名前空間ではなく、std::cout 、std::endl のみを取り込んでいる。
これにより、std::cout 、std::endl は名前空間を明示せずに使用できるが、同様に std 名前空間に属する count() は取り込んでいないため、
名前解決を行う必要がある。
名前空間への別名(エイリアス)付与
名前空間の名称が長い、または階層が深い場合など、名前空間に別名を付与することで簡略化できる。
// 長い名前の very_very_long_namespace に別名 ns を付与
namespace ns = very_very_long_namespace;
// 階層の深い a::b::c::hoge に別名 hoge を付与
namespace hoge = a::b::c::hoge;
無名名前空間(unnamed namespace)
無名名前空間は、名前空間の名称を省略した特殊な名前空間である。
無名名前空間内で宣言されたシンボルは、そのファイルでのみ有効なシンボル(内部リンケージ)となり、ファイルを跨いで参照ができなくなる。
従来はシンボルを static 修飾することで内部リンケージを実現していたが、C++では static よりも無名名前空間を用いることが推奨されている。
(Sample.cpp)
// 無名名前空間
namespace
{
int a = 1; //< 内部リンケージを持つ
}
static int b = 2; //< 内部リンケージを持つ
int c = 3; //< 外部リンケージを持つ
上記の例では、変数 a を無名名前空間内に宣言している。このため、a は Sample.cpp 以外からはアクセスできない。
static 修飾された変数 b も同様である。
従来記法との比較
// 従来の記法
static int a;
static int b;
const int c = 99;
static const char* d;
extern "C" int x = 1;
extern "C" int y = 2;
const char* e;
// 無名名前空間を用いた構造的な記法
namespace
{
int a;
int b;
const int c = 99;
const char* d;
}
extern "C"
{
int x = 1;
int y = 2;
}
const char* e;
無名名前空間を用いることで、記述は簡潔になり、内部リンケージの宣言範囲がわかりやすくなる。
ただしシンボルが極端に多い場合は static 宣言を用いたほうが見やすいこともある。
しかしシンボルが多いのは、そのファイルが抱えている役割が肥大化していることによる場合が多いため、
ファイルを役割ごとに分割するほうが望ましい。