C++ で名前空間を適切に使用するにはどうすればよいでしょうか?
-
09-06-2019 - |
質問
私は Java の出身で、名前空間ではなくパッケージが使用されます。私は、連携して完全なオブジェクトを形成するクラスをパッケージにまとめ、後でそのパッケージから再利用することに慣れています。しかし、今は C++ で作業しています。
C++ で名前空間をどのように使用しますか?アプリケーション全体に対して単一の名前空間を作成しますか、それとも主要コンポーネントに対して名前空間を作成しますか?もしそうなら、他の名前空間のクラスからオブジェクトを作成するにはどうすればよいでしょうか?
解決
名前空間は本質的にはパッケージです。それらは次のように使用できます。
namespace MyNamespace
{
class MyClass
{
};
}
次に、コード内で次のようにします。
MyNamespace::MyClass* pClass = new MyNamespace::MyClass();
それが役立つことを願っています。
または、常に特定の名前空間を使用したい場合は、次のようにすることもできます。
using namespace MyNamespace;
MyClass* pClass = new MyClass();
編集: 何を追いかけて ベルンハルドルシュ が言ったように、私は「using namespace x」構文をまったく使用しない傾向があり、通常、オブジェクトをインスタンス化するときに名前空間を明示的に指定します(つまり、私が最初に示した例)。
そしてあなたが尋ねたように 下に, 、名前空間は好きなだけ使用できます。
他のヒント
すべてを語るのを避けるために、Mark Ingram はすでに名前空間を使用するためのちょっとしたヒントを述べています。
ヘッダー ファイル内で "using namespace" ディレクティブを使用しないでください。これにより、このヘッダー ファイルをインポートするプログラムのすべての部分に対してネームスペースが開かれます。実装ファイル (*.cpp) では、これは通常大きな問題ではありませんが、私は関数レベルで "using namespace" ディレクティブを使用することを好みます。
名前空間は主に名前の競合を避けるために使用され、必ずしもコード構造を整理するために使用されるわけではないと思います。私は C++ プログラムを主にヘッダー ファイル/ファイル構造で編成します。
大規模な C++ プロジェクトでは、実装の詳細を隠すために名前空間が使用されることがあります。
using ディレクティブへの追加の注意:単一の要素に対してのみ「using」を使用することを好む人もいます。
using std::cout;
using std::endl;
ヴィンセント・ロバートのコメントは正しい C++ で名前空間を適切に使用するにはどうすればよいでしょうか?.
ネームスペースの使用
名前空間は、少なくとも名前の衝突を避けるために使用されます。Java では、これは「org.domain」イディオムによって強制されます (ユーザーは自分のドメイン名以外は使用しないと想定されているため)。
C++ では、モジュール内のすべてのコードに名前空間を与えることができます。たとえば、モジュール MyModule.dll の場合、そのコードに名前空間 MyModule を指定できます。MyCompany::MyProject::MyModule を使用している人を他の場所で見たことがあります。これはやりすぎだと思いますが、全体的にはそれが正しいように思えます。
「使って」を使う
を使用すると、名前空間から 1 つ (またはすべて) のシンボルが現在の名前空間に効率的にインポートされるため、使用には細心の注意が必要です。
これをヘッダー ファイル内で行うのは邪悪です。ヘッダーがそれを含むすべてのソースを汚すことになるからです (マクロを思い出します...)。また、ソース ファイル内であっても、グローバル スコープでインポートされるため、関数スコープ外では悪いスタイルになります。名前空間からのシンボル。
「using」を使用する最も安全な方法は、選択シンボルをインポートすることです。
void doSomething()
{
using std::string ; // string is now "imported", at least,
// until the end of the function
string a("Hello World!") ;
std::cout << a << std::endl ;
}
void doSomethingElse()
{
using namespace std ; // everything from std is now "imported", at least,
// until the end of the function
string a("Hello World!") ;
cout << a << endl ;
}
多くの「名前空間STDを使用」が表示されます。チュートリアルまたはサンプルコード。その理由は、シンボルの数を減らして読みやすくするためであり、それが良いアイデアだからではありません。
「名前空間STDを使用。」スコット・マイヤーズには落胆しています(どの本を正確に覚えていませんが、必要に応じて見つけることができます)。
名前空間の構成
名前空間は単なるパッケージではありません。別の例は、Bjarne Stroustrup の「The C++ Programming Language」にあります。
「特別版」では、 8.2.8 名前空間の構成, では、AAA と BBB という 2 つの名前空間を CCC という名前の別の名前空間にマージする方法について説明しています。したがって、CCC は AAA と BBB の両方のエイリアスになります。
namespace AAA
{
void doSomething() ;
}
namespace BBB
{
void doSomethingElse() ;
}
namespace CCC
{
using namespace AAA ;
using namespace BBB ;
}
void doSomethingAgain()
{
CCC::doSomething() ;
CCC::doSomethingElse() ;
}
異なる名前空間から選択したシンボルをインポートして、独自のカスタム名前空間インターフェイスを構築することもできます。これの実用的な使い方はまだ見つかっていませんが、理論的には素晴らしいものです。
他の回答ではそれについての言及がなかったので、ここに私の2カナダセントがあります。
「名前空間の使用」トピックに関して、便利なステートメントは名前空間エイリアスです。これを使用すると、名前空間の「名前を変更」できます (通常は、より短い名前を付けることができます)。たとえば、次の代わりに:
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;
あなたは書ける:
namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;
名前空間は単なる名前空間であるという人々の意見に耳を貸さないでください。
これらは、コンパイラによってインターフェイスの原則を適用するとみなされるため、重要です。基本的には、次の例で説明できます。
namespace ns {
class A
{
};
void print(A a)
{
}
}
A オブジェクトを出力したい場合、コードは次のようになります。
ns::A a;
print(a);
関数を呼び出すときに名前空間について明示的に言及しなかったことに注意してください。これがインターフェースの原則です。C++ では、型を引数として受け取る関数は、その型のインターフェイスの一部であるとみなされるため、パラメーターにはすでに名前空間が暗示されているため、名前空間を指定する必要はありません。
では、なぜこの原則が重要なのでしょうか?クラス A の作成者がこのクラスに print() 関数を提供しなかったと想像してください。自分で用意する必要があります。あなたは優れたプログラマであるため、この関数を独自の名前空間、または場合によってはグローバル名前空間で定義するでしょう。
namespace ns {
class A
{
};
}
void print(A a)
{
}
そして、コードはどこからでも print(a) 関数の呼び出しを開始できます。ここで、数年後、作成者がクラスの内部構造を知っており、あなたのバージョンよりも優れたバージョンを作成できるため、作成者よりも優れた print() 関数を提供することに決めたと想像してください。
その後、C++ の作成者は、インターフェイスの原則を尊重するために、別の名前空間で提供されている関数の代わりに、彼のバージョンの print() 関数を使用する必要があると決定しました。そして、print() 関数のこの「アップグレード」はできるだけ簡単であるべきです。つまり、print() 関数の呼び出しをすべて変更する必要はありません。そのため、C++ では名前空間を指定せずに「インターフェイス関数」(クラスと同じ名前空間にある関数) を呼び出すことができます。
だからこそ、C++ 名前空間を使用する場合は、それを「インターフェイス」として考慮し、インターフェイスの原則を念頭に置く必要があります。
この動作についてより詳しく説明したい場合は、次の書籍を参照してください。 Herb Sutter の優れた C++
より大きな C++ プロジェクトでは、複数の名前空間が使用されているのをほとんど見たことがありません (例:ブーストライブラリ)。
実際、boost は大量の名前空間を使用します。通常、boost のすべての部分が内部動作用に独自の名前空間を持ち、トップレベルの名前空間 boost にはパブリック インターフェイスのみが配置される場合があります。
個人的には、コードベースが大きくなるほど、単一のアプリケーション (またはライブラリ) 内であっても、名前空間の重要性が高まると考えています。仕事では、アプリケーションの各モジュールを独自の名前空間に配置します。
私がよく使う名前空間のもう 1 つの用途 (冗談ではありません) は、匿名名前空間です。
namespace {
const int CONSTANT = 42;
}
これは基本的に次と同じです。
static const int CONSTANT = 42;
ただし、C++ の現在のコンパイル単位内でのみコードとデータを表示するには、(静的ではなく) 匿名名前空間を使用することをお勧めします。
また、名前空間に追加できることにも注意してください。これは例で見るとより明確になります。つまり、次のことが可能です。
namespace MyNamespace
{
double square(double x) { return x * x; }
}
ファイルの中に square.h
, 、 そして
namespace MyNamespace
{
double cube(double x) { return x * x * x; }
}
ファイルの中に cube.h
. 。これは単一の名前空間を定義します MyNamespace
(つまり、複数のファイルにわたって単一の名前空間を定義できます)。
Javaの場合:
package somepackage;
class SomeClass {}
C++ の場合:
namespace somenamespace {
class SomeClass {}
}
そしてそれらを使用して、Java は次のようになります。
import somepackage;
そしてC++:
using namespace somenamespace;
また、完全な名前は、Java の場合は「somepackge.SomeClass」、C++ の場合は「somenamespace::SomeClass」です。これらの規則を使用すると、名前空間に一致するフォルダー名を作成するなど、Java で慣れているように整理できます。ただし、フォルダー -> パッケージおよびファイル -> クラスの要件はないため、パッケージや名前空間から独立してフォルダーとクラスに名前を付けることができます。
たとえば、関数内に「using namespace ...」を含めることもできます。
void test(const std::string& s) {
using namespace std;
cout << s;
}
@マリウス
はい、一度に複数の名前空間を使用できます。例:
using namespace boost;
using namespace std;
shared_ptr<int> p(new int(1)); // shared_ptr belongs to boost
cout << "cout belongs to std::" << endl; // cout and endl are in std
【2月2014 -- (本当にそんなに経ったのですか?):ジョーイが以下で指摘するように、この特定の例は現在あいまいです。ブーストと標準::それぞれがshared_ptrを持つようになりました。]
一般的に、関数名または型名が他のライブラリと競合する可能性があると思われる場合は、コード本体の名前空間を作成します。ブランドコードにも役立ちます ブースト:: .
私は、アプリケーションにはトップレベルの名前空間を使用し、コンポーネントにはサブ名前空間を使用することを好みます。
他の名前空間のクラスを使用する方法は、驚くほど Java での方法とよく似ています。「import PACKAGE」ステートメントと同様の「use NAMESPACE」を使用できます。標準を使用します。または、パッケージをクラスのプレフィックスとして「::」で区切って指定します。std::文字列。これは、Java の「java.lang.String」に似ています。
C++ の名前空間は実際には単なる名前空間であることに注意してください。これらは Java でパッケージが行うカプセル化を提供しないため、おそらくあまり使用しないでしょう。
私は C#、Perl などと同じ方法で C++ 名前空間を使用してきました。これは、標準ライブラリのもの、サードパーティのもの、および私自身のコード間のシンボルのセマンティックな分離にすぎません。私は、独自のアプリを 1 つの名前空間に配置し、その後、再利用可能なライブラリ コンポーネントを別の名前空間に配置して分離します。
Java と C++ のもう 1 つの違いは、C++ では名前空間の階層がファイルシステムのレイアウトと一致する必要がないことです。そのため、私は再利用可能なライブラリ全体を単一の名前空間に配置し、ライブラリ内のサブシステムをサブディレクトリに配置する傾向があります。
#include "lib/module1.h"
#include "lib/module2.h"
lib::class1 *v = new lib::class1();
名前が競合する可能性がある場合にのみ、サブシステムをネストされた名前空間に配置します。