|
比較的最近登場したC++の機能の中に、「ネームスペース(名前空間)」というものがあります(シェルエクステンションの「ネームスペース」とは別物です)。この機能はさっそくSTLに使用されているので、存在くらいは知っていることでしょう。
今回は、どういう機能を持ったものなのか、簡単に見ていこうと思います。 |
|
「ネームスペース」とは |
|
クラス、関数、変数、構造体などの「名前」には、これまで多くのプログラマーが悩まされてきました。これらの名前には「見ただけでどんなものか判る」「他の名前とかち合わない」「「長すぎずコーディングしやすい」といったことが求められます。プログラマーはこれらを満たす「命名規則」に沿ってコードを書きます。が、それはしばしばそのプログラマー独自のもので、「すべてのプログラマーが満足する」名前というものは存在しませんでした。
C++の「ネームスペース」は、「命名規則」の手助けになるものです。#defineやtypedefといったものを使用せず、言語として「判りやすく汎用性がある名前」を作成できるようサポートする機能、それがネームスペースです。 たとえば、あなたのライブラリで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を省略することができます。 usingとusing 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() );
}
|
フルネームでの使用はもちろんできます。これがもっとも確実な方法です。
ネームスペースの省略も行えます。まず、「省略」だけなら、NameAとNameB、同時に行えます。先ほど説明したように、省略形が使えるというだけで、実際の名前は変わっていないからです。 でも、さすがに両方とも省略した状態でCTestと省略形で書くことは許されません。NameA::CTestとNameB::CTest、どちらが使用されるのか分からないからです(コピーコンストラクタの指定も意味がありません)。この場合には、フルネームで書けばOKです。 このように、ネームスペースとクラスや関数を組み合わせることで、「名前がかち合う」という問題を簡単に解決できます。フルネームでも、省略形でも書けるわけで、コーディングも楽になるでしょう。 |
|
まとめ&次回予告 |
|
以上で分かるとおり、ネームスペースは柔軟で使いやすい機能です。ネームスペースを使った命名法を行えば、分かりやすく変更しやすいコードを書くことができるでしょう。
次回は、このネームスペースをフルに活用する方法を紹介しようと思います。使うか使わないかは、皆さん次第ですが……。 |
| (C)KAB-studio 1999 ALL RIGHTS RESERVED. |