ネームスペース
 
 比較的最近登場したC++の機能の中に、「ネームスペース(名前空間)」というものがあります(シェルエクステンションの「ネームスペース」とは別物です)。この機能はさっそくSTLに使用されているので、存在くらいは知っていることでしょう。
 今回は、どういう機能を持ったものなのか、簡単に見ていこうと思います。
 
 「ネームスペース」とは
 
 クラス、関数、変数、構造体などの「名前」には、これまで多くのプログラマーが悩まされてきました。これらの名前には「見ただけでどんなものか判る」「他の名前とかち合わない」「「長すぎずコーディングしやすい」といったことが求められます。プログラマーはこれらを満たす「命名規則」に沿ってコードを書きます。が、それはしばしばそのプログラマー独自のもので、「すべてのプログラマーが満足する」名前というものは存在しませんでした。
 
 C++の「ネームスペース」は、「命名規則」の手助けになるものです。#definetypedefといったものを使用せず、言語として「判りやすく汎用性がある名前」を作成できるようサポートする機能、それがネームスペースです。
 
 たとえば、あなたのライブラリでGet()という関数を作成したいと考えます。でも、この名前はあまりにもストレートすぎます。これまでは、こういう場合__Get()MyGet()のような名前を付けてきました。ですが、ネームスペースでは違います。
 ネームスペースが使える場合にはMy::Get()という名前にすることができます。ネームスペースの便利なところは、必要ならGet()とシンプルに関数を呼び出すこともできるという点です。つまり「名前の短縮がサポートされている」ということです。
 
 これらは変数、クラス、関数など、さまざまなものに適用でき、また、様々なものにまとめて名前を付けることができます。
 では、その実際の方法を見ていきましょう。
 
 「名前」で囲む
 
 クラスや関数を名前で囲むときには、次のようにnamespaceというキーワードを使用します。
 


//////////////////////////////////
// ヘッダーファイル内。
namespace NameA	//"NameA"という名前で囲みます。
{
class CTest
{
public:
	void Test1();
	void Test2()
	{
		TRACE0( "NameA::CTest::Test2()\n" );
		return;
	}
};

void Test();

}	//namespace NameA	//ここまで囲みました。


//////////////////////////////////
// ソースファイル内。
void NameA::CTest::Test1()
{
	TRACE0( "NameA::CTest::Test1()\n" );
	return;
}


namespace NameA	//"NameA"という名前で囲みます。
{
void Test()
{
	TRACE0( "NameA::Test()\n" );
	return;
}
}	//namespace NameA

	
 
 まず「ヘッダーファイル」の方を見てください。「NameA」という「名前」で囲んであります。この囲んだ中が「NameA というネームスペース」ということになります。
 ネームスペース内のオブジェクトは「ネームスペースの名前::オブジェクト名」という名称になります。上の例だと、「NameA::CTest」とか「NameA::Test()」という名前になります。
 
 実装時にはそれを踏まえる必要があります。CTest::Test1()NameA::CTest::Test1()という名前で書く必要があります。また、その下のNameA::Test()の例のように、実装部も同じネームスペースで囲んでもOKです。これと同じ原理で、ヘッダーファイルでインライン関数として実装しても構いません。
 この例で分かるように、「同じ名前のネームスペースは同じネームスペースとして認識される」ことになります。たとえば、上のヘッダーファイルの例は次のコードと同じ意味になります。
 


//////////////////////////////////
// ヘッダーファイル内。
namespace NameA	//"NameA"という名前で囲みます。
{
class CTest
{
public:
	void Test1();
	void Test2()
	{
		TRACE0( "NameA::CTest::Test2()\n" );
		return;
	}
};
}	//namespace NameA	//ここまで囲みました。

namespace NameA	//"NameA"という名前で囲みます。
{
void Test();

}	//namespace NameA	//ここまで囲みました。

	
 
 ネームスペースをいくつかに分けても、同じ名前なら同じネームスペースです。
 また、ネームスペースはグローバルなオブジェクトしか囲めません。たとえば、関数内やクラス内ではネームスペースを作ることはできません。ですが、ネームスペースをさらにネームスペースで囲むこと、つまり「ネームスペースのネスト」はできます。
 


//////////////////////////////////
// ヘッダーファイル内。
namespace NameA	//"NameA"という名前で囲みます。
{
namespace NameB	//"NameB"という名前で囲みます。
{
class CTestZ	//NameA::NameB::CTestZ という名前になりました。
{
/*
	namespace BadName	//これはダメ!
	{
		//...
	}
*/
};
}	//namespace NameB	//ここまで囲みました。
}	//namespace NameA	//ここまで囲みました。

	
 
 ネームスペースのネストを行うと、それだけ名前が増えていきます。「面倒なだけやん」とか思うでしょうが、後で説明するように、これは使い勝手を向上させるでしょう。
 あ、ネームスペースのインデントについては、現在考慮中です。今のところ「しない方がいい」と考えています。インデントが深くなること自体、あまり好きでないもので……。
 
 「フルネーム」で使う
 
 では、これらのクラスや関数を使ってみることにします。まずは「省略しない」方法です。
 


// ソースコード内。
// ここはネームスペースで囲まれていません。

void UseNameA()
{
	NameA::CTest cTest;
	cTest.Test1();
	cTest.Test2();
	NameA::Test();
}

	
 
 フルネームで呼び出すときは実装部分の場合と同じように「ネームスペースの名前::オブジェクト名」という形で指定します。これは関数でもクラスでも同じです。これだけです。簡単でしょう。
 
 「省略名」で使う
 
 今度は「ネームスペースの名前」を省略して使ってみることにしましょう。省略するときにはusingというキーワードを使用します。
 


// ソースコード内。
// ここはネームスペースで囲まれていません。

void UseNameA2()
{
	using NameA::CTest;	//省略を指定します。
	CTest cTest;	//すると、省略形で書けます。
	cTest.Test1();
	cTest.Test2();
//	Test();	//指定してないのはダメ。
	using namespace NameA;	//これで"NameA"を省略できます。
	Test();	//今度はOK。
	NameA::Test();	//これもちゃんと使えます。
//	::Test();	//これはダメ!!

	// ここで using と using namespace の効力は切れます。
}

	
 
 usingを使用すると、特定のオブジェクトだけを省略形で書くことができます。上の例だと、NameA::CTestを省略形で書くことができますが、NameA::Test()は省略形では書けません。
 using namespaceなら、特定のネームスペースを省略することができます。これだと、NameAの名前が付いたオブジェクトはすべてNameAを省略することができます。
 usingusing namespaceは、これらを使用したスコープの中でしか効力を持ちません。逆に言えば、グローバル空間で使用すると、どこででも省略できることになります。これをうまく使わないと、ネームスペースの意味がなくなってしまうので気を付けてください。
 
 注意して欲しいのは、using namespaceを使って指定したネームスペースがグローバルになるわけではないということです。
 単に「省略できるだけ」というだけで、省略しない名称も使えます。また、名前の付いてない「::」だけの指定はできません。この指定は「どのネームスペースにも囲まれてないオブジェクト」を指定するからです。
(だから、これまでAPIに::を着けてたのは、ちょっとまずかったのかも……)
 
 同一名のオブジェクトを使用する
 
 さて、ここでもっとも標準的な「ネームスペースの効用」を利用してみましょう。
 前述のNameA::CTestの他に、もうひとつCTestを作りたい、という場合には、これもネームスペースで囲んでしまえばいいわけです。
 


//////////////////////////////////
// ヘッダーファイル内。
namespace NameA	//"NameA"という名前で囲みます。
{
class CTest
{
public:
	void Test1();
	void Test2()
	{
		TRACE0( "NameA::CTest::Test2()\n" );
		return;
	}
};
}	//namespace NameA	//ここまで囲みました。

namespace NameB	//"NameB"という名前で囲みます。
{
class CTest
{
public:
	CTest( int p_i )	//コンストラクタです。
		: m_i( p_i )
	{}
	
	int Get()
	{
		return m_i;
	}

private:
	int m_i;
};
}	//namespace NameB	//ここまで囲みました。

	
 
 このように、同じ名前のクラスを別々のネームスペースに囲むことで、クラス名について気にする必要がなくなります。
 これらのクラスは、次のように使用できます。
 


void UseNameAnB()
{
	// フルネームで使用。
	NameA::CTest cTestA;
	NameB::CTest cTestB( 10 );
	cTestA.Test2();
	TRACE( "%d\n", cTestB.Get() );

	// NameA だけ using namespace します。
	using namespace NameA;
	CTest cTestA2;
	cTestA2.Test2();

	// NameB も using namespace します。
	using namespace NameB;	//これは通ります。
//	CTest cTestB2( 100 );	//これはダメ。あいまいです。
	NameB::CTest cTestB2( 100 );	//しょーがないからフルネーム指定。
	TRACE( "%d\n", cTestB2.Get() );
}

	
 
 フルネームでの使用はもちろんできます。これがもっとも確実な方法です。
 ネームスペースの省略も行えます。まず、「省略」だけなら、NameANameB、同時に行えます。先ほど説明したように、省略形が使えるというだけで、実際の名前は変わっていないからです。
 でも、さすがに両方とも省略した状態でCTestと省略形で書くことは許されません。NameA::CTestNameB::CTest、どちらが使用されるのか分からないからです(コピーコンストラクタの指定も意味がありません)。この場合には、フルネームで書けばOKです。
 このように、ネームスペースとクラスや関数を組み合わせることで、「名前がかち合う」という問題を簡単に解決できます。フルネームでも、省略形でも書けるわけで、コーディングも楽になるでしょう。
 
 まとめ&次回予告
 
 以上で分かるとおり、ネームスペースは柔軟で使いやすい機能です。ネームスペースを使った命名法を行えば、分かりやすく変更しやすいコードを書くことができるでしょう。
 次回は、このネームスペースをフルに活用する方法を紹介しようと思います。使うか使わないかは、皆さん次第ですが……。
(C)KAB-studio 1999 ALL RIGHTS RESERVED.