関連する一連の関数を持っている、または作成しようとしているとしましょう。それらが数学に関連しているとしましょう。組織的には、次のことを行う必要があります。
- これらの関数を記述し、
MyMath
名前空間に配置して、経由で参照しますMyMath::XYZ()
- というクラス
MyMath
を作成し、これらのメソッドを静的にして、同様に参照します。MyMath::XYZ()
ソフトウェアを整理する手段として、どちらか一方を選択する必要があるのはなぜですか?
関連する一連の関数を持っている、または作成しようとしているとしましょう。それらが数学に関連しているとしましょう。組織的には、次のことを行う必要があります。
MyMath
名前空間に配置して、経由で参照しますMyMath::XYZ()
MyMath
を作成し、これらのメソッドを静的にして、同様に参照します。MyMath::XYZ()
ソフトウェアを整理する手段として、どちらか一方を選択する必要があるのはなぜですか?
デフォルトでは、名前空間関数を使用します。
クラスはオブジェクトを構築するためのものであり、名前空間を置き換えるものではありません。
Scott Meyers は、このトピックに関する彼の効果的な C++ の本のアイテム全体を書きました。Herb Sutter の記事でこの原則に関するオンライン リファレンスを見つけました: http://www.gotw.ca/gotw/084.htm
C++ では、クラスと同じ名前空間にあり、そのクラスをパラメーターとして持つ関数は、そのクラスのインターフェイスに属します(関数呼び出しを解決するときに ADLがこれらの関数を検索するため) 。.
例えば:
- 名前空間Nがあるとしましょう
- 名前空間Nで宣言されたクラスCがあるとします(つまり、そのフルネームはN::Cです) 。
- 名前空間Nで宣言された関数Fがあるとします(つまり、そのフルネームはN::Fです) 。
- 関数Fのパラメーターの中に、型Cのパラメーターがあるとします。
... 次に、N::FはN::Cのパブリック インターフェイスの一部です。
名前空間関数は、"friend" と宣言されていない限り、クラスの内部にアクセスできませんが、静的メソッドにはアクセスできます。
これは、たとえば、クラスを保守するときに、クラスの内部を変更する必要がある場合、静的メソッドを含むすべてのメソッドで副作用を検索する必要があることを意味します。
クラスのインターフェースにコードを追加します。
C# では、アクセス権がなくてもクラスにメソッドを追加できます。しかし、C++ では、これは不可能です。
しかし、C++ では、誰かが作成したクラスであっても、名前空間付きの関数を追加できます。
反対側から見ると、これはコードを設計するときに重要です。関数を名前空間に配置することで、ユーザーがクラスのインターフェイスを増やしたり完成させたりすることを許可するからです。
前のポイントの副作用として、複数のヘッダーで静的メソッドを宣言することはできません。すべてのメソッドは、同じクラスで宣言する必要があります。
名前空間の場合、同じ名前空間の関数を複数のヘッダーで宣言できます (ほぼ標準の swap 関数がその最良の例です)。
名前空間の基本的なクールな点は、一部のコードでは、キーワードを使用すると名前空間に言及することを避けることができるということです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
この「パターン」は、ほぼ標準的な swap イディオムを適切に使用するために必須です。
そして、これはクラスの静的メソッドでは不可能です。
そのため、C++ 名前空間には独自のセマンティクスがあります。
ただし、継承と同様の方法で名前空間を組み合わせることができるため、さらに先に進みます。
たとえば、関数を含む名前空間、関数を含む名前空間がある場合A
、名前空間を宣言し、キーワードを使用してこの名前空間におよび を取り込むことができます。AAA
B
BBB
C
AAA
BBB
using
名前using namespace
空間 D!
namespace A
{
void AAA();
void AAA2();
}
namespace B
{
void BBB();
}
namespace C
{
using A::AAA;
using B::BBB;
}
namespace D
{
using namespace A;
using namespace B;
}
void foo()
{
C::AAA();
// C::AAA2(); // ERROR, won't compile
C::BBB();
}
void bar()
{
D::AAA();
D::AAA2();
D::BBB();
}
名前空間は名前空間用です。クラスはクラスのためのものです。
C++ は、それぞれの概念が異なるように設計されており、さまざまな問題の解決策として、さまざまなケースでさまざまな方法で使用されます。
名前空間が必要な場合は、クラスを使用しないでください。
あなたの場合、名前空間が必要です。
賛否両論あると思いますが、私は以下のように考えています。
クラスは本質的に、特定の種類のオブジェクトの定義です。静的メソッドは、そのオブジェクト定義に密接に関連する操作を定義する必要があります。
基礎となるオブジェクトまたは一種のオブジェクトの定義に関連付けられていない関連する関数のグループを作成するだけの場合は、名前空間のみを使用することをお勧めします。私にとっては、概念的には、これははるかに賢明です。
たとえば、あなたの場合、「MyMath とは何ですか?」と自問してください。MyMath
がオブジェクトの種類を定義していない場合は、クラスにしないでください。
しかし、私が言ったように、(特に Java と C# の開発者は) 私に反対する人がたくさんいることを知っています (特に Java と C# の開発者)。
それ以外の場合は、名前空間関数を使用してください。
コメントへの回答: はい、静的メソッドと静的データは過剰に使用される傾向があります。そのため、役立つと思われる関連するシナリオを 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;
それが必要ない場合は、必ず名前空間を使用してください。
クラスを使用するもう 1 つの理由 - アクセス指定子を利用するためのオプション。次に、パブリック静的メソッドをより小さなプライベート メソッドに分割することができます。パブリック メソッドは、複数のプライベート メソッドを呼び出すことができます。