web-dev-qa-db-ja.com

C ++でクラススコープ定数を宣言/定義する場所

C++のさまざまな定数宣言と定義オプションのメリット/デメリットに興味があります。長い間、クラス定義の前にヘッダーファイルの先頭でそれらを宣言してきました。

//.h
const int MyConst = 10;
const string MyStrConst = "String";
class MyClass {
...
};

これはグローバル名前空間を汚染しますが(悪いことですが、それが悪い理由のランドリーリストを見つけたことはありません)、定数はまだ個々の翻訳単位にスコープされますので、このヘッダーを含まないファイルこれらの定数にはアクセスできません。しかし、他のクラスが同じ名前の定数を定義している場合、名前の衝突が発生する可能性があります。これは、リファクタリングが可能な領域を適切に示している可能性があるため、おそらく悪いことではありません。

最近、クラス定義自体の中でクラス固有の定数を宣言する方が良いと判断しました。

//.h
class MyClass {
    public:
         static const int MyConst = 10;
...
    private:
         static const string MyStrConst;
...
};
//.cpp
const string MyClass::MyStrConst = "String";

定数の可視性は、定数がクラスの内部でのみ使用されるか、クラスを使用する他のオブジェクトに必要かによって調整されます。これは、主に内部クラス定数をクラスにプライベートに保つことができ、パブリック定数を使用する他のクラスは定数のソースへのより詳細な参照を持っているためです(例えばMyClass: :MyConst)。また、グローバル名前空間を汚染しません。ただし、cppファイルで非整数の初期化が必要になるという欠点があります。

また、定数を独自のヘッダーファイルに移動し、他のクラスが定数を必要とし、クラス定義全体を必要としない場合に、それらを名前空間にラップすることも検討しました。

意見や、おそらく私がまだ検討していなかった他のオプションを探しているだけです。

59
bsruth

静的なクラスメンバーとして非整数定数を宣言すると、「cppファイルで非整数の初期化が必要になる」という主張は、厳密には言えません。 cppファイルでの定義が必要ですが、それは「損害」ではなく、あなたの意図の問題です。 C++の名前空間レベルconstオブジェクトには、デフォルトで内部リンケージがあります。つまり、元のバリアントでは宣言が

const string MyStrConst = "String"; 

と同等です

static const string MyStrConst = "String"; 

つまり、このヘッダーファイルが含まれるすべての翻訳単位で、独立したMyStrConstオブジェクトを定義します。これを知っていますか?これはあなたの意図でしたか?

いずれにせよ、具体的にがすべての翻訳単位で個別のオブジェクトを必要としない場合、元の例のMyStrConst定数の宣言は良い習慣ではありません。通常、ヘッダーファイルには非定義宣言のみを配置します。

extern const string MyStrConst; 

cppファイルで定義を提供します

const string MyStrConst = "String";

したがって、プログラム全体が同じ定数オブジェクトを使用するようにします。言い換えれば、非整数定数に関しては、通常はcppファイルで定義することです。そのため、クラス内またはクラス外で宣言する方法に関係なく、通常はcppファイルで定義しなければならない「不利益」に常に対処する必要があります。もちろん、上記で述べたように、名前空間定数を使用すると、最初のバリアントにあるものを回避できますが、それは単なる「遅延コーディング」の一例にすぎません。

とにかく、問題を過度に複雑にする理由はないと思います。定数にクラスへの明らかな「アタッチメント」がある場合、クラスメンバとして宣言する必要があります。

追伸アクセス指定子(publicprotectedprivate)は、名前のvisibilityを制御しません。 accessibilityのみを制御します。どのような場合でも名前は表示されたままです。

45
AnT

グローバルネームスペースの汚染は、誰か(たとえば、使用するライブラリの作成者)が名前MyConstを別の目的に使用する可能性があるため、不良です。これは、重大な問題(一緒に使用できないライブラリなど)につながる可能性があります。

定数が単一のクラスにリンクされている場合、2番目のソリューションは明らかに最適です。それがそれほど簡単ではない場合(プログラム内のクラスに関係のない物理定数または数学定数を考える)、名前空間ソリューションはそれよりも優れています。ところで:古いC++コンパイラと互換性がなければならない場合、それらのいくつかはヘッダーファイルで積分初期化を使用できないことに注意してください-この場合、C++ファイルで初期化するか、古いenumトリックを使用する必要があります。

私は定数のためのより良いオプションはないと思います-少なくとも現時点では考えられません...

10
hjhill

グローバル名前空間を汚染することは自明のことです。ヘッダーファイルを含める場合、そのヘッダーで宣言された定数との名前の競合を検出またはデバッグしたくありません。これらのタイプのエラーは本当にイライラし、時には診断が困難です。たとえば、これをヘッダーに定義されているプロジェクトにリンクする必要がありました。

#define read _read

定数が名前空間の汚染である場合、これは名前空間の核廃棄物です。この症状は、_read関数がないことを訴える一連の非常に奇妙なコンパイラエラーですが、そのライブラリに対してリンクしている場合にのみ発生しました。最終的に、読み取り関数の名前を別の名前に変更しました。これは難しくはありませんが、不要なはずです。

2番目のソリューションは、変数をスコープに入れるので非常に合理的です。これをクラスに関連付ける必要はありません。クラス間で定数を共有する必要がある場合は、独自の名前空間とヘッダーファイルで定数を宣言します。これはコンパイル時には適していませんが、必要な場合があります。

私はまた、人々が自分のクラスに定数を置き、それをシングルトンとして実装するのを見てきました。私にはこれは報酬なしで機能しているようです。この言語は定数を宣言するための機能を提供します。

5
James Thompson

これらの定数を使用するクラスが1つだけの場合は、static constクラス本体内。多数の関連クラスが定数を使用する場合は、定数とユーティリティメソッドのみを保持するクラス/構造内で、または専用の名前空間内で宣言します。例えば、

namespace MyAppAudioConstants
{
     //declare constants here
}

それらがアプリケーション全体(またはそのかなりの部分)で使用される定数である場合は、(暗黙的または明示的に)どこにでも含まれるヘッダーの名前空間内で宣言します。

namespace MyAppGlobalConstants
{
    //declare constants here
}
2
Chinmay Kanchi

ヘッダーで参照されていない限り、c ++ファイルでそれらをグローバルとして宣言できます。その後、それらはそのクラスに対してプライベートであり、グローバル名前空間を汚染しません。

2
Alex Brown

個人的には、2番目のアプローチを使用します。私は何年も使ってきましたが、私にとってはうまく機能します。

可視性の点から、実装ファイルの外に誰も存在する必要がないので、私はプライベート定数ファイルレベルの静的を作成する傾向があります。これは、名前のスコープが使用スコープと同じであるため、名前を変更したり新しい名前を追加する必要がある場合、連鎖反応の再コンパイルを防ぐのに役立ちます...

2
Len Holgate