C ++で名前空間を適切に使用するにはどうすればよいですか?


231

私は名前空間ではなく、パッケージが使用されるJavaのバックグラウンドから来ています。私は、一緒に機能するクラスをパッケージにまとめて完全なオブジェクトを作成し、それらを後でそのパッケージから再利用することに慣れています。しかし、今はC ++で作業しています。

C ++で名前空間をどのように使用しますか?アプリケーション全体に単一の名前空間を作成しますか、それとも主要コンポーネントに名前空間を作成しますか?もしそうなら、他の名前空間のクラスからオブジェクトをどのように作成しますか?

回答:


167

名前空間は本質的にパッケージです。これらは次のように使用できます。

namespace MyNamespace
{
  class MyClass
  {
  };
}

次にコードで:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

または、常に特定の名前空間を使用する場合は、次のようにします。

using namespace MyNamespace;

MyClass* pClass = new MyClass();

編集:bernhardruschの発言に従って、「using namespace x」構文をまったく使用しない傾向があります。通常、オブジェクトをインスタンス化するときに名前空間を明示的に指定します(つまり、最初に示した例)。

また、以下で尋ねたように、名前空間はいくつでも使用できます。


25
IMO使用するstdよりも、名前空間をシンボルの前に付けることに慣れる方が良いusingです。私はいつも書くように、std::coutあるいはstd::string今、それは私が今、彼らを呼んだから。私は決して書くつもりはありませんcout
トム・サベージ

5
これはに非常に当てはまりstdますが、小さなライブラリを扱う場合、個人的にはそれほど重要ではないことがわかりました。多くの場合using namespace FooBario;、特にライブラリからかなりの数の型を使用している場合は、だけを使用できます。
jkerian

4
@jkerian、私はあなたの要点を理解していますが、名前の衝突は(私の心の中で)正確にそのような小さなライブラリから生じる可能性が高いため、私は同意しません。ほとんどの人は、クラス/関数にSTLのものと同じ名前を付けないように注意しています。そうは言っても、using namespace X;可能であればヘッダーファイルで回避する必要があることに同意します。
アランチューリング、

12
@LexFridman「ほとんどの人はSTLのクラス/関数と同じ名前を付けないように注意しています」-これは真実ではありません。たとえば、奇妙なハードウェア用に非常に特殊なI / Oコードを作成する場合mylibrary::endl、自分の特別な改行シーケンスを表す以外に何も使用しません。つまり、なぜ名前を発明するのですか?

名前空間を明示的に指定し、宣言されたファイルを含めても、コンパイラーはまだ名前空間を認識しません。
bgenchel 2015年

116

マークイングラムが名前空間を使用するための小さなヒントを既に言ったことをすべて避けるために:

ヘッダーファイルで「using namespace」ディレクティブを使用しないでください。これにより、このヘッダーファイルをインポートするプログラムのすべての部分のネームスペースが開きます。実装ファイル(* .cpp)では、これは通常大きな問題ではありません。ただし、関数レベルで「using namespace」ディレクティブを使用することを好みます。

名前空間は、名前の競合を回避するために主に使用されていると思います。コード構造を必ずしも整理するためではありません。主にヘッダーファイル/ファイル構造を使用してC ++プログラムを整理します。

より大きなC ++プロジェクトでは、実装の詳細を隠すために名前空間が使用されることがあります。

usingディレクティブに関する補足:一部の人々は、単一の要素に対してのみ「using」を使用することを好みます。

using std::cout;  
using std::endl;

2
.cpp内の.cppファイルレベルまたは名前空間{}ブロックレベルではなく、関数レベルで「名前空間を使用する」ことの利点の1つは、単一のコンパイルユニットのビルドに非常に役立つことです。「名前空間の使用」は推移的であり、同じユニット内の個別の名前空間A {}ブロック全体の名前空間Aに適用されるため、単一のコンパイルユニットビルドでは、ファイルまたは名前空間ブロックレベルで行われた場合、すべてをすぐに使用してしまいます。
idij 2014

using std::cout; は使用宣言です
Konstantin

3
1つのステートメントで1つの名前空間から複数の名前を使用することは可能ですか?何かのように、あるいは、。using std::cout, std::endl;using std::cout, endl;
AlQuemist 2016

using namespace x別の名前空間内にある場合は、ヘッダーでを使用しても問題ありません。これは私がお勧めするものではありませんが、グローバル名前空間を汚染することはありません。
Praxeolitic 2016年

79

Vincent Robertは彼のコメントのとおりです。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 ;
}

多くの「using namespace std;」が表示されます。チュートリアルまたはサンプルコード。理由は、シンボルの数を減らして読みやすくするためです。

"名前空間stdを使用しています。" スコット・マイヤーズはお勧めしません(どの本を正確に覚えていませんが、必要に応じて見つけることができます)。

名前空間の構成

名前空間はパッケージだけではありません。別の例は、Bjarne Stroustrupの「The C ++ Programming Language」にあります。

「特別版」の8.2.8名前空間の構成では、2つの名前空間AAAとBBBを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つの関数は1つの名前空間にある必要がありますか?
yanpas

74

他の回答ではそれについての言及は見られなかったので、ここにカナダの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;

55

名前空間は単なる名前空間であると言っているすべての人々の言うことを聞かないでください。

これらはコンパイラによってインターフェイスの原則を適用すると見なされているため、重要です。基本的には、例で説明できます。

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 ++名前空間を使用する場合は、C ++名前空間を「インターフェイス」と見なし、インターフェイスの原則に留意する必要があります。

この動作の詳細な説明が必要な場合は、本 『ハーブサッターの例外C ++』を参照してください。


23
ns :: Printが追加されている場合、実際にはprint()へのすべての呼び出しを変更する必要がありますが、コンパイラは各呼び出しにあいまいなフラグを付けます。静かに新しい機能に切り替えるのはひどい考えです。
Eclipseの

@Vincentがすべての呼び出しをprintに変更する必要があると言っていることを考えているのですが、もしautorがns :: Print()関数を提供する場合、何を言おうとしていたのでしょうか。著者がns :: Print()関数を追加したときに、独自の実装を削除できますか?または、ns :: print()using-declarationを使用して追加するだけですか?または他の方法ですか?ありがとう
Vaska el gato

36

私が見たより大きなC ++プロジェクトでは、複数の名前空間(例:boostライブラリ)をほとんど使用していません。

実際にboostは大量の名前空間を使用します。通常、boostのすべての部分は内部の仕組みのために独自の名前空間を持ち、パブリックインターフェースのみをトップレベルの名前空間boostに配置する場合があります。

個人的には、単一のアプリケーション(またはライブラリ)内でも、コードベースが大きくなるほど、名前空間が重要になると思います。作業では、アプリケーションの各モジュールを独自の名前空間に配置します。

私が頻繁に使用する名前空間のもう1つの使用法(しゃれた意図はありません)は、匿名の名前空間です。

namespace {
  const int CONSTANT = 42;
}

これは基本的に次と同じです:

static const int CONSTANT = 42;

ただし、静的ではなく匿名の名前空間を使用することは、C ++の現在のコンパイルユニット内でのみコードとデータを表示するための推奨方法です。


13
const int CONSTANT = 42;名前空間スコープのトップレベルのconstは既に内部リンケージを意味しているため、どちらの例も同等です。したがって、この場合は匿名の名前空間は必要ありません。
09

19

また、名前空間に追加できることにも注意してください。これは例を示すとより明確になります。つまり、次のことが可能です。

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

ファイル内square.h、および

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

ファイル内cube.h。これは単一の名前空間を定義しますMyNamespace(つまり、複数のファイルにわたって単一の名前空間を定義できます)。


11

Javaの場合:

package somepackage;
class SomeClass {}

C ++の場合:

namespace somenamespace {
    class SomeClass {}
}

そしてそれらを使用して、Java:

import somepackage;

そしてC ++:

using namespace somenamespace;

また、フルネームは、Javaの場合は「somepackge.SomeClass」、C ++の場合は「somenamespace :: SomeClass」です。これらの規則を使用して、名前空間に一致するフォルダー名を作成するなど、Javaで慣れているように整理できます。ただし、フォルダー->パッケージおよびファイル->クラスの要件はありません。そのため、パッケージと名前空間から独立して、フォルダーとクラスに名前を付けることができます。


6

@ マリウス

はい、一度に複数の名前空間を使用できます。例:

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-(本当に長いのか?):Joeyが以下で指摘するように、この特定の例はあいまいです。Boostとstd ::にはそれぞれshared_ptrがあります。]


2
注意stdもありshared_ptr、今では、その両方を使用boostしてstd、使用しようとすると、名前空間が衝突しますshared_ptr
ジョーイ14

2
これは、多くのソフトウェア会社が名前空間全体をこの方法でインポートすることを阻止する理由の良い例です。常に名前空間を指定しても問題はありません。長すぎる場合は、名前空間からエイリアスまたは重要な特定のクラスのみを作成します。
水田

5

たとえば、関数内に「using namespace ...」を含めることもできます。

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

3

一般的に言って、他のライブラリと関数または型の名前が競合している可能性があると思われる場合は、コード本体の名前空間を作成します。また、コードのブランド化、ala boost ::にも役立ちます。


3

アプリケーションにはトップレベルの名前空間を使用し、コンポーネントにはサブ名前空間を使用することを好みます。

他の名前空間のクラスを使用する方法は、Javaの方法と驚くほどよく似ています。stdを使用するなど、「import PACKAGE」ステートメントに似た「use NAMESPACE」を使用することもできます。または、「::」で区切られたクラスの接頭辞としてパッケージを指定します(例:std :: string)。これは、Javaの「java.lang.String」に似ています。


3

C ++の名前空間は実際には単なる名前空間であることに注意してください。これらは、パッケージがJavaで行うカプセル化を提供しないため、おそらくあまり使用しません。


2

私はC#、Perlなどで使用するのと同じようにC ++名前空間を使用しました。これは、標準ライブラリスタッフ、サードパーティスタッフ、および自分のコード間のシンボルの意味上の分離にすぎません。自分のアプリを1つの名前空間に配置し、再利用可能なライブラリコンポーネントを別の名前空間に配置して分離します。


2

JavaとC ++のもう1つの違いは、C ++では名前空間階層がファイルシステムのレイアウトを変更する必要がないことです。そのため、再利用可能なライブラリ全体を単一の名前空間に配置し、ライブラリ内のサブシステムをサブディレクトリに配置する傾向があります。

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

名前が競合する可能性がある場合にのみ、サブシステムをネストされた名前空間に配置します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.