名前空間
名前空間とは
C++では名前空間という概念が導入されています。
名前空間は、名前の衝突(同じ名前を付けることで呼び出し先が曖昧になること)を避けるために有効に働きます。
例えば、「stdio.h」には「remove関数」が存在します。
そのため、プログラマが「remove」という名前の自作関数を作ると、stdio.hにあるremove関数なのか自作関数のremove関数なのか、見分けがつかなくなります。
C言語では同じ名前の関数を作ることはできませんが、C++にはオーバーロードという機能があるため、名前が同じでも引数が違えば定義は可能です。
しかし、標準関数なのか自作関数なのか見分けが付きにくくなるので、同じ名前の関数は避けるべきです。
(関数のオーバーロードについてはC++の関数で説明します)
名前空間は、関数などの「名前」が存在する「住所」を定義するようなものです。
同じ「山田さん」でも、「1丁目の山田さん」と「2丁目の山田さん」は別の人であると識別できます。
この「1丁目の」「2丁目の」が名前空間に当たります。
先ほどのremove関数の例で言うと、それぞれを「stdio.hのremove関数」「自作関数のremove関数」と呼ぶことで、別の関数と識別することができます。
文字列出力に使用した「std::cout」は、「stdという名前空間に存在するcout」という意味となります。
「::」はスコープ解決演算子(大域解決演算子)と呼ばれます。
スコープ解決演算子という名前からもわかる通り、名前空間は変数のスコープとよく似ています。
(スコープについては変数のスコープ(C言語)参照)
変数が存在する場所によって、現在の場所から参照できるかできないかが決まります。
名前空間は、参照できない場所からでも参照できるように住所を知らせてあげるようなものです。
using namespace
coutを使用したければ「std::cout」と記述するわけですが、毎回これを記述していては少し面倒です。
「std::」はまだ短いので楽ですが、もっと長い名前空間や階層の深い場所だと大変になります。
これを解決するためにはusing宣言を使用します。
#include <iostream>
using namespace std;
int main()
{
//「std::」を省略できる
cout << "ABCDE" << endl;
getchar();
}
コードの冒頭で「using namespace std;」と宣言することで、コード中では「std::」を省略することができます。
using namespace std;の弊害
「using namespace std;」はコードの冒頭に記述しておく、としている解説がありますが、サンプルコードのような小規模なものならばともかく、実際のコードでは冒頭でこれを宣言することはあまりありません。
「std」に存在する名前群は非常に数が多く、中には普通に変数や関数名に付けてしまいそうな名前も割と多いのです。
例えばstdには「count」という、配列内の特定の要素数を数える関数があります。
(iostream内に定義されています)
countというわかりやすい名前は普通に使ってもおかしくありませんが、「using namespace std;」を記述していると名前がややこしくなります。
#include <iostream>
using namespace std;
int count()
{
//何か処理
return 0;
}
int main()
{
int arr[] = { 0, 1, 0, 2 };
int ret1, ret2;
//自作関数count
ret1 = count();
//std::count
ret2 = count(arr, arr + sizeof(arr), 0);
cout << ret1 << endl;
cout << ret2 << endl;
}
このコードでは引数が異なるので関数オーバーロードにより同じ名前でも定義は可能ですが、引数が同じ場合はエラーになりますし、そもそもどちらが呼び出されているのかが分かりづらくバグの原因にもなり得ます。
これではせっかく名前空間で識別できるのに、意味がありません。
これを解決するためには、省略したい名前だけを明示する方法があります。
#include <iostream>
//coutとendlだけ「std::」を省略して書ける
using std::cout;
using std::endl;
int count()
{
//何か処理
return 0;
}
int main()
{
int arr[] = { 0, 1, 0, 2 };
int ret1, ret2;
//自作関数count
ret1 = count();
//std::count
//「std::」は省略できない
ret2 = std::count(arr, arr + sizeof(arr), 0);
cout << ret1 << endl;
cout << ret2 << endl;
}
「using std::cout;」とすると、coutだけは「std::」を省略できるようになります。
明示していないものは省略ができないので、名前空間により別の関数であると識別ができるようになります。
ちなみに、「using namespace」はコードの冒頭以外にも、関数の中に記述することもできます。
関数内に記述すると、その関数内でだけ名前空間の識別が有効になります。
#include <iostream>
void message(char *s)
{
//この関数内だけ「std::」を省略できる
using namespace std;
cout << s << endl;
}
int main()
{
//「std::」は省略できない
std::cout << "ABC" << std::endl;
message("DEF");
}
namespaceの定義
namespaceは自分で定義することもできます。
#include <iostream>
namespace myApp
{
using namespace std;
void printTest()
{
cout << "myApp::test" << endl;
}
namespace nest
{
void printTest()
{
cout << "myApp::nest::test" << endl;
}
}
}
int main()
{
myApp::printTest();
myApp::nest::printTest();
std::cin.get();
}
namespaceという修飾子に続いて、好きな名前空間名を記述します。
その後の波括弧ブロック内{}が新しい名前空間となります。
名前空間の中にさらに名前空間を定義することもできます。
それぞれ別の名前空間に属しますから、同じ名前の関数を定義することもできます。
他人に公開するライブラリなどを作成する場合は、namespaceを使わないと名前空間がごっちゃになるので定義しておくべきです。