私はたくさんのJavaコーディングを最近行い、非常に特定のパッケージネーミングシステムに慣れてきました。たとえば、com.company.project.db
。これは、Java、AS3/Flex、およびC#で正常に機能します。 C++にも同じパラダイムが適用されているのを見てきましたが、C++名前空間をJavaパッケージに直接対応するものと見なすのは悪いことだと聞いています。
それは本当ですか、そしてなぜですか?名前空間/パッケージはどのように似ていますか?深くネストされた名前空間を使用する場合、どのような問題が発生する可能性がありますか?
C++では、名前空間は使用可能な名前をパーティション化するためのものです。 Javaパッケージはモジュールに関するものです。ネーミング階層はその1つの側面にすぎません。
C++の深くネストされた名前空間には、何も問題はありませんが、背後にモジュールシステムがなく、余分なレイヤーがノイズを追加するだけなので、通常は必要ありません。通常は、1つまたは2つのレベルの名前空間があれば十分です。内部の詳細(通常、単に「詳細」と呼ばれることもあります)の奇妙な追加レベルがあります。
argument-dependent-lookup など、使いすぎた場合に注意を引く可能性のあるC++名前空間の追加のルールと、親レベルへの解決に関するルールもあります。後者については、次のようにします。
namespace a{ namespace b{ int x; } }
namespace b{ string x; }
namespace a
{
b::x = 42;
}
これは合法ですか?何が起こっているのかは明らかですか?これらの質問に答えるには、名前空間解決の優先順位を知る必要があります。
Javaパッケージはネストされておらず、フラットです。明らかなネストは、命名規則にすぎません。
たとえば、パッケージcom.company.project.db
は、com.company.project
またはcom.company.project.db.x
とはまったく関係がありません。 com.company.project.db
のコードは、com.company.project.db.x
のコードよりもa.b.c
のコードにアクセスできません。
C++の名前空間がJavaまたはC#の名前空間と異なる理由と理由は次のとおりです。
競合の回避
Java/C#言語では、名前空間は、クラスライブラリの異なる部分にある名前間の競合を回避することを目的としています。 C#の名前空間階層の5つの異なる場所に「Watcher」というクラスがある場合があります。 C++では、ライブラリ内に同じ名前のクラスが存在する場合、名前空間を作成する代わりに別のクラス内に配置します。このようなネストされたクラスはすべて適切であり、推奨されています。実際、構文は::演算子を使用してクラスが名前空間であるかのように処理します。
入れ子になった名前空間はいくつあるべきですか?
Boost、Eigen、そしてもちろんSTLのような人気のあるライブラリが良い例です。これらのライブラリには通常、std::
やboost::
やeigen::
のような1つの名前空間にほとんどすべてが詰め込まれています。 std::ios
やboost:filesystem
のような独自の名前空間を取得するコンポーネントはほとんどありません。 2番目のレベルをいつ使用するかに関して一貫した規則はありませんが、大規模であるか、個別に開発/保守されているか、オプションのコンポーネントは通常、独自の名前空間を取得しています。 3番目のレベルはさらに珍しいです。一般に、私は構造company::project
を使用し、プロジェクトの独立して使用可能な大規模なサブシステムcompany::project::component
を使用します。
外部ライブラリでの競合の回避
さて、大きな問題:名前空間とクラスがまったく同じである2人の異なる人から2つのライブラリを取得した場合はどうなるでしょうか。ほとんどの人は少なくともプロジェクトの名前でライブラリをラップする傾向があるため、この状況はかなりまれです。プロジェクト名が同じであっても、両方のライブラリを使用することはまれです。しかし、時にはプロジェクト名(ahm ... "metro"、 "apollo" ...)について誤った決定が下されることや、名前空間でさえまったく使用されないことがあります。これが発生した場合は、いずれかまたは両方のライブラリの#includeを名前空間にラップし、競合を解決します!これは、紛争の解決が簡単であるため、人々が紛争についてあまり気にしない理由の1つです。 company::project
を使用する慣習に従っている場合、競合は非常にまれになります。
言語の違い
C++はC#と同じようにusing namespace
ステートメントを提供しますが、独自の名前空間にすべてを「インポート」することは、一般的に悪い習慣と見なされています。この理由は、ヘッダーには、あなたを完全に驚かせる可能性のあるものを再定義することを含め、多くの「悪い」ものを含めることができるためです。これは、using namespace
と同等の操作を行った場合にのみクリーンなパブリックインターフェイスを取得するC#/ Javaとはかなり異なります。 (補足:C++では、Pimplパターンを使用して同じことを実現できますが、通常は余分な配管が多すぎて、実際に実行するライブラリはほとんどありません)。したがって、using namespace
を実行することはほとんどありません。代わりに、実際に使用したいものに対してtypedefs
(またはusing name =
)を実行します。これも、深くネストされた名前空間を使用することを非現実的にしています。
組織化コード
Java/C#では、コードをフォルダに整理する傾向があります。通常、フォルダーが20ファイルまたは10ファイルを超えると、人々はフォルダーについて考え始めます。 C++では、状況は もっと多様 ですが、多くの大規模プロジェクトでは、よりフラットなディレクトリ構造が推奨されます。たとえば、標準ライブラリの stdフォルダー には53個のファイルがあり、Facebookの folly プロジェクトは同じルートを進んでいるようです。これの理由の1つは、ワイルドカードを使用してフラット構造のファイルを見つけることができるコンソールとは対照的に、Java/C#の人々がビジュアルIDEをより多く使用し、フォルダーナビゲーションでマウススクロールを使用するという事実だと思います。また、C++プログラマーは、C#やJavaとは異なり、複数のクラスを単一のファイルに入れ、ファイルを論理ユニットとしてクラス名と同じではなく論理ユニットとして命名することを絶対に避けません。これにより、コンパイルが高速になります。これは、大規模なプロジェクトでは非常に重要です。各フォルダーに独自の名前空間を持たせるための言語レベルの要件はありませんが、多くのC++開発者は、各フォルダーに独自の名前空間を割り当て、フォルダー階層を2レベル以下に保つことを好みます。
例外の可能性があります
C++では、すでにA::B::C::D
の内部にいる場合は、C::D
をA::B
として参照できます。したがって、プライベートコードまたは使用頻度の低いクラスをさらにプッシュしたい場合は、独自の相対的な深さを2程度に保ちながら、そうすることができます。この場合、ファイルの場所を予測できるように、各レベルのフォルダーを作成することもできます。一般に、この領域にはゴールドスタンダードはありませんが、C#/ Javaを模倣した深くネストされた名前空間を使いすぎたくないでしょう。
関連
以前の答えには欠けているものがあると思いますが、それがC++が本当に好きな理由の1つです。
グラフィカルアプリケーションをプログラミングしていて、突然、すべてのウィジェットに共通点があることに気付いたとします。あなたはそれらすべてに新しい機能を持たせたいです。職業はなんですか?
1)基本ウィジェットクラスを編集しますか?大丈夫ですが、おそらくあなたはそれにアクセスできません。多分あなた自身の変更をすることを妨げるライセンス問題がある。あなたがそれを行うことができたとしても、それがあなたのプロジェクトにとって意味のあるものであるなら、作者は彼らの将来のリリースにそれを含めないでしょう、そしてツールキットのアップグレードはより苦痛になるでしょう
2)インターフェースクラス/マルチ継承を作成しますか?既存のコードに応じて、ウィジェットに関連するすべての単一クラスを更新するのは多少困難になります。これを行うと、新しいクラスを定義するすべての人が、自分のインターフェースから継承することを想定していることを知っている必要があるため、コードの保守コストが高くなります。他の人々の規律に依存することは本当に危険です。
ここでのC++名前空間のすばらしい点は、既存のシステム内にstuffをカプセル化する追加の方法があることです。編集できない既存のライブラリ内にカプセル化できるだけでなく、クラス/オブジェクトの階層に簡単に挿入できない同様の概念をカプセル化できます。
Javaはあなたに純粋なOOPデザインにもっと集中することを強制します。確かに私はあなたが汚いハックでエレガントではないかもしれないと言っていますが、修正に時間を費やさない多くの怠惰な人々のプログラミングがあります彼らのデザイン。
C++では名前空間をネストできます。
ただし、Javaと同じようには機能しません。 Javaパッケージは本当にはるかによく定義されており、実際の静的initの奇妙さはありません。C++では名前空間は役立ちますが、それでもかなりの危険が伴います。
C++では、設計と実装の基本単位は名前空間ではなくクラスです。名前空間は、概念を表現するためではなく、大規模なライブラリでの名前の衝突を防ぐ手段として意図されていました。
クラスには、名前空間に比べていくつかの利点があります。
ただし、深くネストされた関係については2度見ます。これは実際にはソフトウェアを設計する良い方法ではなく、クラスや名前空間をjuseするかどうかに関係なく、コードを読み取ることができません。
聞いたことがいくつかありますが、その真実性はわかりません。確認/駆除してください。
C++のパフォーマンスは、(何らかの形で)完全に指定された長いメソッド名の影響を受けます。例:namespace1::namespace2::namespace3::classX::method123()
コンパイラ/リンカーで許可されているシンボル長の制限に達する可能性があります