名前空間+関数とクラスの静的メソッド


290

関連する関数のセットを持っている、または作成しようとしているとしましょう。それらが数学に関連しているとしましょう。組織的には、

  1. これらの関数を記述してMyMath名前空間に配置し、次のように参照しますMyMath::XYZ()
  2. 呼び出されるクラスを作成し、MyMathこれらのメソッドを静的にして、同様に参照しますMyMath::XYZ()

ソフトウェアを整理する手段として、なぜどちらかを選択するのですか?


4
1つには、名前空間は、「C with classes」と呼ばれた当時の言語のクラスや静的メソッドと比較して、言語に最近追加されたものです。一部のプログラマーは、古い機能に慣れている場合があります。他の一部のプログラマーは古いコンパイラーを使用している可能性があります。ちょうど私の$ .02
Rom

21
@Rom:あなたは「古いプログラマ」については正しいですが、「古いコンパイラ」については間違っています。名前空間は古くから正しくコンパイルされています(1998年からVisual C ++ 6で作業していました!)。"C with classes"に関しては、このフォーラムの一部の人々は、それが起こったときに生まれもしませんでした。これを引数として使用して、標準の広範なC ++機能を回避するのは誤りです。結論として、廃止されたC ++コンパイラのみが名前空間をサポートしていません。それらを使用しない言い訳としてその引数を使用しないでください。
paercebal 2009

@paercebal:一部の古代のコンパイラは、組み込みの世界でまだ使用されています。名前空間をサポートしないことはおそらく、誰もが毎日操作するさまざまな小さなCPU(ステレオ、電子レンジ、車内のエンジンコントロールユニット、信号機など)のコードを書くときに我慢する必要がある最小の不便の1つです。はっきりさせてください。私はどこでもより良い、より新しいコンパイラーを使用しないことを主張していません。Au conrare:私はすべて(RTTIを除いて)最新の言語機能に夢中です;))。私はそのような傾向が存在することを指摘しています
Rom

13
@Rom:現在のケースでは、質問の作成者が選択できるため、名前空間付きコードのコンパイルに失敗したコンパイラはありません。また、これはC ++に関する質問なので、必要に応じて名前空間や問題のRTTIソリューションについて言及するなど、C ++の回答を提示する必要があります。Cの回答、またはC-with-classes-for-obsolete-compilersの回答を与えることはトピック外です。
paercebal 2011年

2
「ちょうど私の$ .02」-名前空間をサポートしていない現存するC ++コンパイラの証拠を提供しておけばさらに価値があります。「一部の古くからあるコンパイラーは、まだ組み込みの世界で使用されています」-これはばかげています。「組み込み世界」は、C ++名前空間よりも最近の開発です。「クラス付きC」は、誰もがCコードを埋め込むずっと前に、1979年に導入されました。「私はそのような傾向が存在することを指摘しているだけです」-たとえそうであっても、それはこの質問とは何の関係もありません。
ジムBalter

回答:


243

デフォルトでは、名前空間関数を使用します。

クラスはオブジェクトを構築するためのものであり、名前空間を置き換えるものではありません。

オブジェクト指向コード

Scott Meyersは、このトピックに関する彼の「Effective C ++」本の「メンバー以外の非フレンド関数をメンバー関数よりも優先する」の項目全体を書きました。Herb Sutterの記事で、この原則に関するオンライン参照を見つけました。http://www.gotw.ca/gotw/084.htm

知っておくべき重要なことは、クラスと同じ名前空間のC ++関数では、そのクラスのインターフェイスに属しますADLは関数呼び出しを解決するときにこれらの関数を検索するため)。

名前空間のある関数は、「friend」と宣言されていない限り、クラスの内部にはアクセスできませんが、静的メソッドにはアクセスできます。

これは、たとえば、クラスを維持するときに、クラスの内部を変更する必要がある場合は、静的なものも含め、すべてのメソッドで副作用を検索する必要があることを意味します。

拡張I

クラスのインターフェースにコードを追加する。

C#では、クラスにアクセスできない場合でも、クラスにメソッドを追加できます。しかし、C ++では、これは不可能です。

ただし、C ++でも、名前空間付きの関数を追加できます。誰かが作成したクラスにも追加できます。

反対側から見ると、これはコードを設計するときに重要です。名前空間に関数を配置することにより、ユーザーにクラスのインターフェイスの増加/完成を許可するためです。

拡張II

前のポイントの副作用として、複数のヘッダーで静的メソッドを宣言することは不可能です。すべてのメソッドは同じクラスで宣言する必要があります。

名前空間の場合、同じ名前空間の関数を複数のヘッダーで宣言できます(ほとんど標準のスワップ関数がその最も良い例です)。

拡張III

名前空間の基本的なクールさは、一部のコードでは、キーワード "using"を使用する場合、その言及を避けることができるということです。

#include <string>
#include <vector>

// Etc.
{
   using namespace std ;
   // Now, everything from std is accessible without qualification
   string s ; // Ok
   vector v ; // Ok
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

また、「汚染」を1つのクラスに制限することもできます。

#include <string>
#include <vector>

{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

この「パターン」は、ほぼ標準のスワップイディオムを適切に使用するために必須です。

そして、これをクラスの静的メソッドで行うことは不可能です。

したがって、C ++名前空間には独自のセマンティクスがあります。

しかし、継承と同様の方法で名前空間を組み合わせることができるため、さらに進んでいきます。

たとえば、名前空間Aに関数AAAがあり、名前空間Bに関数BBBがある場合、名前空間Cを宣言し、キーワードus​​ingを使用してこの名前空間にAAAとBBBを組み込むことができます。

結論

名前空間は名前空間用です。クラスはクラス用です。

C ++は、各概念が異なるように設計されており、さまざまな問題の解決策として、さまざまなケースでさまざまに使用されています。

名前空間が必要な場合はクラスを使用しないでください。

そしてあなたの場合、名前空間が必要です。


この回答はスレッドにも適用できますか?つまり、スレッドには静的メソッドではなく名前空間を使用する方が良いですか?
dashesy

3
@dashesy:名前空間と静的メソッドはスレッドとは関係がないため、名前空間は静的メソッドよりもほとんど常に優れているため、名前空間の方が優れています。静的メソッドはクラスメンバー変数にアクセスできるため、名前空間よりもカプセル化の値が低くなっています。また、スレッド化された実行では、データの分離がさらに重要です。
paercebal 2012

@ paercebal-おかげで、私はスレッド関数に静的クラスメソッドを使用していました。私はクラスを名前空間として誤用していることを理解したので、1つのオブジェクトに複数のスレッドを含めるための最良のアプローチは何だと思いますか?私もSOについてこの質問をしました。いくつかの光(ここまたは質問自体)を当てることができるかどうかに感謝します
ダッシュ

1
@dashesy:あなたは問題を求めています。異なるスレッドで必要なのは、共有されないはずのデータを分離することです。そのため、クラスのプライベートデータへの特権アクセスを複数のスレッドに持たせることは悪い考えです。クラス内で1つのスレッドを非表示にし、そのスレッドのデータをメインスレッドのデータから分離するようにします。もちろん、共有されることになっているデータはそのクラスのメンバーになることができますが、それらはまだ同期されている必要があります(ロック、アトミックなど)。どのくらいの数のライブラリにアクセスできるかはわかりませんが、tasks / asyncを使用する方が良いです。
paercebal 2012

paercebalの答えは受け入れられるべきです!名前空間+
ADL-

54

私に反対する人はたくさんいますが、これは私がそれを見る方法です:

クラスは本質的に特定の種類のオブジェクトの定義です。静的メソッドは、そのオブジェクト定義に密接に関連付けられている操作を定義する必要があります。

基礎となるオブジェクトまたは一種のオブジェクトの定義に関連付けられていない関連する関数のグループを作成するだけの場合は、名前空間のみを使用することになります。私にとっては、概念的には、これははるかに賢明です。

たとえば、あなたの場合、「マイマスとは何ですか?」と自問してください。MyMathがオブジェクトの種類を定義していない場合、はこう言います:それをクラスにしないでください。

しかし、私が言ったように、私はこれについて(激しくさえ)私に反対する多くの人々(特にJavaとC#開発者)がいることを知っています。


3
あなたはこれについて非常に純粋な見方をしています。しかし、実質的にすべての静的メソッドを持つクラスが便利になることができ、話す:あなたがすることができtypedef、それらは、テンプレートパラメータなどとしてそれらを使用する
Shog9

56
それは、JaveとC#の人々には選択肢がないためです。
マーティンヨーク

7
@ shog9。関数もテンプレート化できます!
マーティンヨーク

6
@ダン:おそらく、数学ルーチンを必要とし、さまざまな実装の「プラグイン」をサポートしたいと考えたものです。
Shog9 2009

1
@ダン:「誰かがクラスをテンプレートパラメータとして使用することに興味がある場合、そのクラスは基本的に何らかの基礎となるオブジェクトを定義していると思います。」いいえ、まったくありません。特性について考えてください。(それにもかかわらず、私はあなたの答えに完全に同意します。)
sbi 2009

18
  • 静的データが必要な場合は、静的メソッドを使用します。
  • それらがテンプレート関数であり、すべての関数のテンプレートパラメーターのセットを一緒に指定できるようにしたい場合は、テンプレートクラスで静的メソッドを使用します。

それ以外の場合は、名前空間関数を使用します。


コメントへの応答:はい、静的メソッドと静的データは使い過ぎる傾向があります。そのため、私が役立つと思われる関連シナリオを2つだけ提供しました。OPの特定の例(一連の数学ルーチン)で、すべてのルーチンに適用されるパラメーター(コアデータ型と出力精度など)を指定する機能が必要な場合、彼は次のようなことを行う可能性があります。

template<typename T, int decimalPlaces>
class MyMath
{
   // routines operate on datatype T, preserving at least decimalPlaces precision
};

// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;

それが必要ない場合は、必ず名前空間使用してください。


2
いわゆる静的データは、名前空間の実装ファイル内の名前空間レベルのデータにすることができます。これにより、ヘッダーに表示する必要がないため、結合がさらに削減されます。
モッティ

静的データは、名前空間スコープのグローバルと同じです。
coppro 2009

@coppro。それらは、非公開にすることができる(ただし、同意する)ことができるため、ランダムグローバルからの進化の連鎖の少なくとも1つのステップです。
マーティンヨーク

@Motti:OTOH、ヘッダー(インライン/テンプレート関数)でそれを使用したい場合は、醜いものに戻ります。
Shog9 2009

1
興味深い例、クラスを省略形として使用してtemplate引数の繰り返しを回避する!
underscore_d

13

名前空間にはクラスよりも多くの利点があるため、名前空間を使用する必要があります。

  • 同じヘッダーですべてを定義する必要はありません
  • すべての実装をヘッダーで公開する必要はありません
  • usingクラスのメンバーはできません。using名前空間のメンバーができます
  • あなたはできませんがusing classusing namespaceそれほど良い考えではないこともあります
  • クラスを使用することは、実際には何もないときに作成されるオブジェクトがあることを意味します

私の意見では、静的メンバーは非常に過度に使用されています。ほとんどの場合、これらは本当に必要なものではありません。静的メンバー関数は、おそらくファイルスコープ関数として適しています。静的データメンバーは、グローバルオブジェクトであり、不当な評判があります。


3
「ヘッダーですべての実装を公開する必要はありません」クラスを使用する場合も同様です。
バヌアン

さらに:名前空間を使用している場合、ヘッダーですべての実装を公開することはできません(最終的に、シンボルが複数定義されることになります)。インラインクラスメンバー関数を使用すると、これを実行できます。
Vanuan

1
@Vanuan:ヘッダーで名前空間の実装を公開できます。inlineキーワードを使用して、ODRを満たします。
Thomas Eding

@ThomasEdingは必要ありません!=することができます
Vanuan

3
@Vanuan:を使用する場合、コンパイラによって保証されるのは1つだけinlineであり、関数の本体を「インライン化」するものではありません。の真の(そして標準によって保証されている)目的は、inline複数の定義を防ぐことです。C ++の「1つの定義ルール」についてお読みください。また、リンクされたSOの質問は、ODRの問題ではなくプリコンパイルされたヘッダーの問題のためにコンパイルされませんでした。
Thomas Eding

3

実装ファイルの匿名の名前空間にプライベートデータを含めることができるように、名前空間が望ましいです(そのため、privateメンバーとは対照的に、ヘッダーに表示する必要はありません)。別の利点はusing、名前空間によってメソッドのクライアントが指定をオプトアウトできることですMyMath::


2
クラスを使用して、実装ファイルの匿名の名前空間にプライベートデータを含めることもできます。私があなたの論理に従っているのかわかりません。
Patrick Johnmeyer

0

クラスを使用するもう1つの理由-アクセス指定子を使用するオプション。次に、パブリック静的メソッドをより小さなプライベートメソッドに分割できます。パブリックメソッドは、複数のプライベートメソッドを呼び出すことができます。


6
アクセス修飾子はクールですが、ほとんどのprivateメソッドでさえ、プロトタイプがヘッダーでまったく公開されていない(したがって、非表示のままである)メソッドよりもアクセスしやすくなっています。匿名で名前空間が付けられた関数によって提供されるより良いカプセル化についても触れていません。
paercebal 2012

2
プライベートメソッドは、IMOの場合、実装(cppファイル)で関数自体を隠すよりも劣っており、ヘッダーファイルでは決して公開しません。回答でこれについて詳しく説明し、プライベートメンバーを使用する理由を説明してください。それまでは-1。
nonsensickle 2014年

@nonsensickle多分、彼は、多くの繰り返しセクションを持つマンモス関数を、問題のあるサブセクションをプライベートの後ろに隠しながら安全に分解でき、危険である/非常に注意深い使用が必要な場合に他のサブセクションにアクセスできないようにすることを意味します。
Troyseph

1
@Troysephであっても、.cppファイル内の名前のない名前空間内にこの情報を非表示にして、ヘッダーファイルを読み取る人に余分な情報を提供することなく、その翻訳単位に対して非公開にすることができます。事実上、私はPIMPLイディオムを擁護しようとしています。
nonsensickle 2015年

.cppテンプレートを使用する場合は、ファイルに入れることはできません。
Yay295 2018

0

名前空間とクラスメソッドの両方に用途があります。名前空間にはファイル全体に分散する機能がありますが、関連するすべてのコードを1つのファイルに入れるように強制する必要がある場合、これは弱点です。上記のように、クラスでは、クラスにプライベート静的メンバーを作成することもできます。実装ファイルの匿名の名前空間に含めることもできますが、クラス内に置くよりも大きなスコープです。


「[ものを保存する]実装ファイルの匿名の名前空間では、クラス内に置くよりもスコープが広い」-いいえ、そうではありません。メンバーへの特権アクセスが必要ない場合、名前空間の匿名のものは、匿名のものよりもプライベートですprivate:。特権アクセスが必要であると思われる多くの場合、それを除外することができます。最も「プライベート」な機能は、ヘッダーに表示されない機能です。private:メソッドはこのメリットを享受できません。
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.