C++言語解説:3-2.ネームスペース
2003-08-02

[概要] ネームスペースはC++の中でも比較的新しい機能です。「コードの再利用」を前提
    としたC++プログラミングでは必須となる、変数名/関数名/クラス名のローカライ
    ズ機能について説明します。

[構成]・ネームスペースの必要性
     * 大規模プログラム開発での懸念点
     * グローバルネームスペースとローカルネームスペース
   ・ネームスペースの作成
     * ネームスペースのローカルスコープ
     * ネームスペースのスコープ解決
     * using文
     * 無名ネームスペース


●ネームスペースの必要性

 ◆大規模プログラム開発での懸念点

  ・C++は、Cよりも大規模なコードを開発するために考案されたプログラム言語である
   ことは、本テキストの冒頭で説明したと思います。
  
  ・大規模開発を実現するためにclassを用いて、概念の単位でプログラミングする手法
   を今まで検討してきたわけですが、これは実際の開発現場において「必要な内容だ
   けが見える、中身のよくわからないCode」を多用することにつながります。
  
  ・プログラムコード中では、それぞれのプログラマがユニークな変数名、関数名、ク
   ラス名を名付けますが、例えば数百人規模の開発において、名前の衝突が起きない
   と言い切れるでしょうか。
  
  ・その数百人が統率の取れた組織に属するのであれば、どうにか管理可能かもしれま
   せん。しかしアウトソーシングを進めてサードパーティー製のライブラリを使うこ
   とになった場合
はどうでしょうか。
  
  ・つまり大規模開発でコードの再利用を行うのであれば、変数/関数/classの名前群
   : 名前空間(namespace)管理は「大規模プログラムの開発用言語の機能」として必須
   になるのです。
  
  
 ◆グローバルネームスペースとローカルネームスペース
 
  ・今まで、C++のコード冒頭部に「using namespace std;」を付けてきましたが、これ
   が何を意味するのか考えてみたいと思います。
  
  ・プログラムが利用するのはグローバルネームスペースです。そのプログラムコード
   自身が定義した識別子(関数/変数/class等の名前)集合は、グローバルネームスペー
   スに配置
されます。
  
  ・それでは標準のC++に用意されている識別子は? これらは「std」と名付けられたロ
   ーカルネームスペースで定義
されています。
  
  ・よって using namespace std; とは文字通り「stdネームスペースをプログラムのグ
   ローバルネームスペースで使用する」
という意味になります。
  
  ・ネームスペースはC++の中でも比較的新しい機能であり、古いC++(最近は見たことあ
   りませんが)ではサポートされていません。このようなC++コンパイラで標準C++の機
   能を使用するには
     #include <iostream.h>
   のように「.h」を記述し、「using namespace std;」を削除するように「1-1:学習
   の準備」で説明していますが、これは全てをグローバルネームスペースで定義する
   という意味です。
  
  ・ここからはコンパイラの実装により異なるのですが、恐らく
     #include <iostream>
   の場合は、stdネームスペースで定義する形になり
     #include <iostream.h>
   ではグローバルネームスペースで定義されることになるのでしょう。(*1)

   [List1.ネームスペース宣言の違い]
   ┌───────────────────────────────────┐
    (1)現在のC++記述
     // stdネームスペースで定義し、グローバルに取り込む
     #include <iostream>
     using namespace std; // stdネームスペースを取り込む必要あり
     
     int main() {
       cout << "Hello World" << endl;
       return 0;
     }
    
    (2)古いC++記述
     // 全てグローバルネームスペースで定義
     #include <iostream.h>
     
     int main() {
       cout << "Hello World" << endl;
       return 0;
     }
   └───────────────────────────────────┘
   ┌───────────────────────────────────┐
    [出力結果]
     Hello World
   └───────────────────────────────────┘


●ネームスペースの作成

 ◆ネームスペースのローカルスコープ
 
  ・今まで無意識に使ってきたように、ネームスペースを作るには「namespace」キーワ
   ードを使用します。
   
  ・namespaceはネームスペースのローカルなスコープを作成します。
     namesapce ネームスペース名 {
       // 変数,関数,クラスの宣言
     }
  
  ・例えば、List2のようにSampleクラスを作成した場合、ここで定義したネームスペー
   スSampleNameSpace内では、特に修飾子を付けなくても変数にアクセスすることがで
   きます。

   [List2.ローカルネームスペースの定義]
   ┌───────────────────────────────────┐
    #include <iostream>
    using namespace std;
    
    namespace SampleNameSpace {
      int li_overflow;
      
      class Sample {
        int mi_count;
        
       public:
        Sample(int i_ini) {
          mi_count = i_ini;
        }
        int CountUp(int i_val) {
          mi_count += i_val;
          
          //li_overflowには普通にアクセスできる
          if (mi_count > li_overflow) return mi_count;
          return 0;
        }
        int Value() {
          return mi_count;
        }
      };
    }
    
    int main() {
      SampleNameSpace::li_overflow = 10; //ネームスペース修飾子
      
      SampleNameSpace::Sample objSample(5); //ネームスペース修飾子
      
      if (objSample.CountUp(3)) {
        cout << "OverFlow!!! : " << objSample.Value() << endl;
      } else {
        cout << "No-OverFlow : " << objSample.Value() << endl;
      }

      if (objSample.CountUp(3)) {
        cout << "OverFlow!!! : " << objSample.Value() << endl;
      } else {
        cout << "No-OverFlow : " << objSample.Value() << endl;
      }
    
      return 0;
    }
   └───────────────────────────────────┘
   ┌───────────────────────────────────┐
    [出力結果]
     Hello World
   └───────────────────────────────────┘

 ◆ネームスペースのスコープ解決

  ・しかし、List2を見るとわかるようにSampleNameSpaceの外、ここではグローバルネ
   ームスペースですが、li_overflowへ初期値設定する際に
     SampleNameSpace::li_overflow
   としています。「::」はスコープ解決演算子です。以前、メンバ関数の定義にも使
   用しましたね。
  
  ・SampleNameSpace内のクラスSampleのオブジェクトobjSampleを定義する際にもスコ
   ープ解決演算子を必要としますが、このステートメントによりobjSampleについては
   ネームスペースのスコープが解決されている
ので、以後「::」を必要としません。


 ◆ネームスペース作成の制約
 
  ・ネームスペースは分割して作成することができます。しかし、その定義はある同一
   ネームスペースの中で行わなければなりません。

  
  ・例えば
   ┌───────────────────────────────────┐
     namespace Sapce1 {
      // 宣言1
     }
      :
     namespace Space1 {
      // 宣言2
     }
   └───────────────────────────────────┘
   のように記述した場合、これはグローバルネームスペースの中で行われているので
   問題ありませんが
   ┌───────────────────────────────────┐
     namespace Sapce1 {
       // 宣言1
       namespace Space2 {
        // 宣言1b
       }
     }
      :
     namespace Space1 {
      // 宣言2
     }
     namespace Space2 {
      // 宣言1b
     }
   └───────────────────────────────────┘
   については、Space2が異なるNameSpaceで定義されているのでNGとなります。ただし
   Space2の定義がSpace1のスコープから外れない限り、分割は可能です。
   

 ◆using文
 
  ・今までローカルネームスペース内のリソースを使用するのにスコープ解決演算子を
   使用していましたが、using文を使用することで指定を省略することができます。
  
  ・以下の形式では、SampleNameSpaceの全てのリソースにアクセスできます。ネームス
   ペース全体が取り込まれた形です。
     using namespace SampleNameSpace;
  
  ・次の形式では指定した特定メンバのみに有効です。
     using SampleNameSpace::li_overflow;
  
  ・今まで使用していた using namespace std; はstdネームスペース全体を取り込んで
   いたわけです。もしcinやcoutのみの使用で問題無ければ
     using std::cout;
     using std::cin;
   と記述すれば良いことになります。
 

 ◆無名ネームスペース
 
  ・無名ネームスペース(unnamed namespace)は、それが定義されたファイルの中でのみ
   成立するネームスペースです。
     namespace {
       // 宣言
     }
  
  ・ここで宣言されたリソースは、単一ファイルの中で宣言無しに使用することができ
   ます。
そして異なるファイルからアクセスできません。
  
  ・同じ機能をstaticにより実現できますが、これは本来スコープの制御を目的とした
   ものでは無いので、C++のプログラミングでは無名ネームスペースの使用が推奨され
   ています。


(*1)調べた範囲では、恐らく #include <*.h> の形式ではグローバルネームスペース定義
  という理解で間違い無いようです。

[Revision Table]
 |Revision |Date    |Comments
 |----------|-----------|-----------------------------------------------------
 |1.00   |2003-08-02 |初版
[end]
Copyright(C) 2003 Altmo