C++入門|C++の名前空間

同じ技名でも、流派が違えば別の技。C++の名前空間を使えば、大きなプログラムでも名前の衝突を防ぎ、安心して開発を進められます。

C++を学んでいくと、C言語にはなかった考え方がいくつか登場します。
その中でも大切なもののひとつが、名前空間です。

名前空間は、英語ではnamespaceと呼ばれます。
これは、変数や関数などの名前を、決められた場所に分けて管理するための仕組みです。

C言語では、プログラムが小さいうちは、変数名や関数名が重複することはあまり気にならないかもしれません。
しかし、C++は大規模な開発を意識して作られた言語です。大きなプログラムでは、多くの開発者が同時に作業し、たくさんの変数、関数、クラスを作ります。

そのとき問題になるのが、名前の重複です。

たとえば、ある開発者がpowerという変数を使い、別の開発者もpowerという変数を使いたいとします。
もし同じ場所に同じ名前が存在してしまうと、どちらのpowerなのか分からなくなったり、意図しない値の変更が起きたりする可能性があります。

ドラゴンボール風にたとえるなら、複数の流派に同じ名前の技があるようなものです。

亀仙流にもpowerという修行値があり、惑星戦士流にもpowerという戦闘値があるとします。
どちらも同じ名前のまま同じ修行場に置いてしまうと、どちらの値を使っているのか分からなくなります。

そこで、名前空間を使います。

名前空間は、流派ごとに修行場を分けるような仕組みです。
亀仙流のpower、惑星戦士流のpowerというように、同じ名前でも所属する場所を分ければ、衝突を避けられます。

学ぶ内容意味ドラゴンボール風のイメージ
名前空間名前を分類するための場所流派ごとの修行場
名前の重複同じ名前がぶつかること同じ技名が複数の流派で使われる
グローバル変数プログラム全体で共有される変数全員が見える共通の戦闘力メーター
ローカル変数関数の中だけで使える変数その修行部屋だけの記録
stdC++標準の名前空間標準流派の道場

C++では、標準機能もstdという名前空間の中に整理されています。
coutやcin、stringなども、std名前空間に含まれるものです。

そのため、C++では名前空間の考え方を理解しておくことがとても大切です。

図:名前空間は流派ごとに名前を分ける仕組み

この図が示していること

この図では、名前空間がない場合と、名前空間がある場合の違いを表しています。

名前空間がないと、同じpowerという名前が同じ場所に存在してしまい、どちらを指しているのか分かりにくくなります。
名前空間を使えば、TurtleSchool::powerやPlanetSchool::powerのように、所属する場所を分けて管理できます。

同じ名前でも、どの流派の名前なのかを明確にできる点が、名前空間の大きな役割です。

名前の重複は大規模開発で起こりやすい

小さなプログラムでは、変数や関数の数が少ないため、名前の重複はあまり問題にならないかもしれません。

しかし、大きなアプリケーションでは話が変わります。

多くの開発者が、それぞれの担当部分を作ります。
その中で、score、power、count、data、show、initのような名前は、いろいろな場所で使いたくなります。

もしすべての名前が同じ空間に置かれていたら、名前の衝突が起きやすくなります。

よく使われる名前起こりやすい問題
power戦闘力、電力、処理能力など意味が重なりやすい
scoreゲームスコア、評価点、試験点数などで使われやすい
count回数、個数、カウンターなどで使われやすい
data汎用的すぎて多くの場所で使われやすい

ドラゴンボール風にいえば、大きな修行場に複数の流派が集まっている状態です。

亀仙流、神流、惑星戦士流がそれぞれpowerという名前を使った場合、区別せずに置いてしまうと混乱します。
そこで、流派ごとに場所を分ける必要があります。

C++の名前空間は、このような大規模開発での名前の衝突を防ぐために用意されています。

名前空間がない場合の問題を見てみよう

まずは、名前空間がない場合にどのような問題が起こるのかを見てみましょう。

次のプログラムでは、battlePowerというグローバル変数を、複数の関数から共有しています。
ある関数で値を変更すると、別の関数で見たときにも変更後の値になっています。

プロジェクト/ファイル名: Chap2_05/main.cpp

#include <iostream>

using namespace std;

// プログラム全体で共有されるグローバル変数
int battlePower;

// 関数のプロトタイプ宣言
void turtleTraining();
void planetTraining();

int main(int argc, char** argv) {
    // 最初の戦闘力を設定する
    battlePower = 50;

    cout << "main:battlePower=" << battlePower << endl;

    // 亀仙流の修行処理を呼び出す
    turtleTraining();

    // 惑星戦士流の修行処理を呼び出す
    planetTraining();

    // プログラムが正常に終わったことを示す
    return 0;
}

// 亀仙流の修行処理
void turtleTraining() {
    cout << "turtleTraining:battlePower=" << battlePower << endl;

    // 共有されているbattlePowerの値を変更する
    battlePower = 80;
}

// 惑星戦士流の修行処理
void planetTraining() {
    cout << "planetTraining:battlePower=" << battlePower << endl;
}

実行すると、次のように表示されます。

main:battlePower=50
turtleTraining:battlePower=50
planetTraining:battlePower=80

この結果を見ると、turtleTrainingでbattlePowerを80に変更したあと、planetTrainingでも80が表示されています。

つまり、battlePowerはプログラム全体で1つだけ存在しており、どの関数から見ても同じ値を参照しているということです。

場所表示される値理由
main50最初にbattlePowerへ50を代入した
turtleTraining50変更前のbattlePowerを表示した
planetTraining80turtleTrainingで80に変更されたあとに表示した

このように、グローバル変数はどこからでも使える便利さがあります。
しかし、その反面、どこかで値が変わると、ほかの場所にも影響します。

ドラゴンボール風にたとえるなら、修行場の中央に1つだけ共通の戦闘力メーターが置いてある状態です。
亀仙流の戦士がそのメーターを80に変えると、惑星戦士流の戦士が見ても80になってしまいます。

グローバル変数とは何か

グローバル変数とは、関数の外で定義された変数です。

今回のプログラムでは、次の行がグローバル変数の定義です。

int battlePower;

この変数は、main関数の外にあります。
そのため、main、turtleTraining、planetTrainingのどこからでも使うことができます。

変数の種類定義する場所使える範囲
グローバル変数関数の外プログラム全体
ローカル変数関数の中その関数の中だけ

グローバル変数は、プログラム全体で共有したい値を扱うときには便利です。

ただし、便利だからといって何でもグローバル変数にすると、どこで値が変わったのか分かりにくくなります。
特に大きなプログラムでは、思わぬ場所で値が変更され、不具合の原因になることがあります。

ドラゴンボール風にいうと、グローバル変数は全流派が見られる共通の戦闘力メーターです。
便利ですが、誰でも触れる状態にしておくと、いつ誰が値を変えたのか分かりにくくなります。

ローカル変数とは何か

ローカル変数は、関数の中で定義された変数です。

たとえば、次のような変数はmain関数の中だけで使えるローカル変数です。

int localPower = 50;

ローカル変数は、その関数の中でだけ使えます。
別の関数からは、直接その変数を使うことはできません。

変数の種類特徴
グローバル変数int battlePower;どの関数からでも使える
ローカル変数関数内のint localPower;定義した関数の中だけで使える

ドラゴンボール風にたとえるなら、ローカル変数はその修行部屋だけにある個人用スカウターです。
ほかの部屋の戦士は、その値を直接見ることができません。

ローカル変数を使うと、値の影響範囲を小さくできます。
そのため、プログラムを安全に書きやすくなります。

グローバル変数は値が共有される

今回のサンプルで大切なのは、battlePowerがプログラム全体で共有されているという点です。

battlePower = 50;

main関数で50を代入すると、turtleTrainingでも同じbattlePowerを見ます。

cout << "turtleTraining:battlePower=" << battlePower << endl;

そして、turtleTrainingでbattlePowerに80を代入すると、

battlePower = 80;

そのあとplanetTrainingで表示したときにも80になります。

cout << "planetTraining:battlePower=" << battlePower << endl;

つまり、どの関数にも別々のbattlePowerがあるわけではありません。
同じ1つのbattlePowerを共有しています。

図:グローバル変数は複数の関数から共有される

この図が示していること

この図では、グローバル変数battlePowerが複数の関数から共有されていることを表しています。

main、turtleTraining、planetTrainingは、すべて同じbattlePowerを見ています。
そのため、turtleTrainingでbattlePowerを80に変更すると、planetTrainingで見たときにも80になります。

グローバル変数は便利ですが、どこからでも変更できるため、値の変化を追いにくくなることがあります。

名前空間は名前を所属ごとに分ける仕組み

名前空間を使うと、同じ名前でも所属する場所を分けられます。

たとえば、亀仙流と惑星戦士流の両方でpowerという名前を使いたい場合でも、名前空間を分ければ衝突しません。

考え方としては、次のようになります。

namespace TurtleSchool {
    int power;
}

namespace PlanetSchool {
    int power;
}

この場合、TurtleSchoolのpowerと、PlanetSchoolのpowerは別物です。

名前意味
TurtleSchool::powerTurtleSchool名前空間のpower
PlanetSchool::powerPlanetSchool名前空間のpower

::は、スコープ解決演算子と呼ばれる記号です。
どの名前空間にある名前を使うのかを指定するときに使います。

ドラゴンボール風にたとえるなら、::は流派名と技名をつなぐ札のようなものです。

TurtleSchool::powerなら、亀仙流のpower。
PlanetSchool::powerなら、惑星戦士流のpower。

このように書くことで、同じpowerという名前でも、どこのpowerなのかを明確にできます。

std名前空間も標準機能をまとめる場所

C++でよく見るstdも名前空間です。

coutやcin、stringなどは、std名前空間の中にあります。

本来は、次のように書くことができます。

std::cout << "Hello" << std::endl;

これは、std名前空間にあるcoutとendlを使う、という意味です。

ただし、学習用のサンプルでは、次のように書いてstd名前空間を使いやすくすることがよくあります。

using namespace std;

これを書くと、std::coutではなくcoutと書けるようになります。

書き方意味
std::coutstd名前空間のcoutを使う
std::endlstd名前空間のendlを使う
using namespace std;std名前空間の名前を省略して使いやすくする

ドラゴンボール風にたとえるなら、stdはC++標準流派の道場です。
coutやcin、endl、stringは、その道場に置かれた標準技です。

using namespace std;は、「このプログラムでは標準流派の技をそのまま使います」と宣言しているようなものです。

using namespace std;は便利だが意味を理解して使う

using namespace std;を書くと、coutやcinを短く書けます。

using namespace std;

cout << "C++の名前空間" << endl;

一方、using namespace std;を書かない場合は、次のようにstd::を付けて書きます。

std::cout << "C++の名前空間" << std::endl;

どちらも意味としては、std名前空間のcoutとendlを使っています。

書き方特徴
using namespace std;を使うcoutやendlを短く書ける
std::を付けるどの名前空間のものか明確に分かる

学習中はusing namespace std;を使うことで、コードが短くなり、読みやすくなります。

ただし、大きなプログラムでは、名前の衝突を避けるためにstd::coutのように明示して書くこともあります。
名前空間は、名前の衝突を防ぐための仕組みなので、便利さと明確さのバランスを考えることが大切です。

名前空間があると同じ名前を安全に使いやすい

名前空間を使うと、同じ名前を複数の場所で使いやすくなります。

たとえば、流派ごとにpowerという変数を持たせることを考えてみましょう。

名前空間変数意味
TurtleSchoolpower亀仙流の修行値
PlanetSchoolpower惑星戦士流の戦闘値
GodSchoolpower神流の気の値

同じpowerという名前でも、名前空間が違えば別の変数として扱えます。

ドラゴンボール風にいえば、同じ技名でも流派が違えば別の技として区別できるということです。

名前空間を使わない場合は、同じ名前がぶつかる可能性があります。
名前空間を使えば、所属を分けることで、衝突を避けられます。

図:名前空間で同じ名前を安全に分ける

この図が示していること

この図では、名前空間を使うことで、同じpowerという名前を別々に管理できることを表しています。

TurtleSchool::power、PlanetSchool::power、GodSchool::powerは、すべてpowerという名前を持っています。
しかし、所属している名前空間が違うため、それぞれ別のものとして扱えます。

::を使うと、どの名前空間の名前を使うのかを明確にできます。

名前空間を使うときの基本形

名前空間は、次のような形で書きます。

namespace 名前空間名 {
    変数や関数など
}

たとえば、TurtleSchoolという名前空間を作るなら、次のようになります。

namespace TurtleSchool {
    int power;
}

このpowerを使うときは、次のように書きます。

TurtleSchool::power = 100;
記述意味
namespace TurtleSchoolTurtleSchoolという名前空間を作る
int power;その名前空間の中にpowerを作る
TurtleSchool::powerTurtleSchoolの中のpowerを指定する

このように、名前空間を使うことで、名前の所属を明確にできます。

C++では、大きなプログラムやライブラリで名前空間がよく使われます。
自分で名前空間を作る機会は最初は少ないかもしれませんが、std名前空間はすぐに登場するため、考え方は早めに理解しておくと安心です。

グローバル変数と名前空間の違いを整理する

グローバル変数と名前空間は、どちらもプログラム全体に関わる名前を扱います。
ただし、役割は違います。

グローバル変数は、プログラム全体で共有される変数です。
名前空間は、名前を分類するための場所です。

項目グローバル変数名前空間
目的値をプログラム全体で共有する名前の衝突を避ける
問題点どこからでも変更される可能性がある使い方を理解しないと名前が長く見える
int battlePower;namespace TurtleSchool { ... }
イメージ共通の戦闘力メーター流派ごとの修行場

グローバル変数をたくさん使うと、どこで値が変更されたのか追いにくくなります。
名前空間を使うと、名前の所属が分かりやすくなり、衝突を防ぎやすくなります。

ドラゴンボール風にいえば、グローバル変数は全員が触れる共通メーターです。
名前空間は、流派ごとに道具や技を整理する道場です。

名前空間で覚えておきたいポイント

名前空間を理解するときは、次のポイントを押さえておきましょう。

ポイント内容
名前空間は名前を分ける場所変数や関数などの名前を分類できる
大規模開発で役立つ名前の重複を防ぎやすい
stdも名前空間coutやcinなどはstdに含まれる
::で所属を指定するstd::coutのように書ける
using namespace std;で省略できるcoutと短く書ける
グローバル変数は共有されるどこかで変更すると他の場所にも影響する

名前空間は、C++を学ぶうえでとても大切な考え方です。

最初はstdやusing namespace std;の意味を理解するところからで大丈夫です。
そこから、同じ名前でも名前空間が違えば別物として扱える、という感覚をつかんでいきましょう。

ドラゴンボール風に言えば、名前空間は流派ごとの道場です。
同じpowerという名前でも、亀仙流のpowerなのか、惑星戦士流のpowerなのかを分けて管理できます。

C++では、プログラムが大きくなるほど名前の整理が大切になります。
名前空間を理解しておくことで、今後クラスやライブラリを扱うときにも、コードの意味を落ち着いて読み取れるようになります。