2011-09-25 14 views
2

私は静的変数に関する多くのドキュメントを読んでいます。ここに私が理解していなかった一例があります。クラスの静的変数は、次のように宣言されていることとします静的変数に関する質問

class Something 
{ 
    public: 
    static int s_nValue; 
}; 

int Something::s_nValue = 1; 

int main()  
{  
    Something::s_nValue = 2;  
    std::cout << Something::s_nValue;  
    return 0;  
} 

私の質問は:我々はすでにクラスでs_nvalueを宣言している、そしてなぜ再びそれを再定義するために必要とされますか?前にintと書いていないと、エラーが表示されます。それはなぜそうですか?

+3

これは言語が求めているものですから。 –

+2

[C++静的メンバー変数とその初期化]の重複可能性(http://stackoverflow.com/questions/4547660/c-static-member-variable-and-its-initialization) –

答えて

2

通常のC++プログラムでは、それらのクラスを使用するすべてのソースファイルに含まれるヘッダーにクラスが定義されます。実際にはすべてのコピーを共有することになっているときに、それぞれのソースファイルには静的変数のコピーがあります。これは、1つの定義ルールの違反になります。各オブジェクトは、1つの場所に存在するものとしてのみ定義できます。

したがって、クラス内の変数を宣言すると、この名前と型の変数がどこかに存在することがコンパイラに通知されます。コンパイラにスペースを割り当てるよう指示するものではありません。この時点で、変数はそれを含むソースファイルでは未定義のままです。次に、1つの特定のソースファイル[通常はその特定のクラスの実装ファイル]内で、実際の定義、つまりint Something::s_nValue;行を指定します。これはコンパイラに変数のための領域を割り当てるように要求します。そのため、その場所にのみ存在し、すべてのオブジェクトファイルをリンクするときにはあいまいさはありません。

2

これは、以前に宣言していたとしても、何かを定義するときに、正確な型を指定する必要があるときに、C++の性質です。これはすべての変数と関数に当てはまります。

サイドノート:再定義しないと、コンパイルエラーが発生します。あなたはそれを定義するだけです。

1

C++の素晴らしい世界へようこそ:宣言VS.定義。投稿したコードには、宣言と定義があります。 宣言は、いくつかのシンボルに名前とタイプを与えます。 定義は、シンボルに「値」を与えます。

class Something 
{ 
public: 
    // declaration. 
    static int s_nValue; 
}; 

// definition. 
int Something::s_nValue = 1; 

このプロセスは、プロトタイピング機能と同様である:混乱に追加するには

// declaration. 
void f (int i); 

// definition. 
void f (int i) 
{ 
    std::cout << i << std::endl; 
    // ... 
} 

を、いくつかのステートメントは、同時に両方を行います。たとえば、関数を宣言しない場合、定義は宣言としても機能します(これは、投稿したSomething::s_nValueのように静的変数では不可能です)。

1

これは、あなたはどうしたらあなたのヘッダファイルにC、の場合と同様である:

extern int Something_s_nValue; 

そして、あなた、あなたはどうしたらあなたのソースファイル:

int Something_s_nValue; 

最初の部分があります宣言はヘッダーファイルにあり、2番目の部分はソースファイルにある定義です。

2

何かを宣言することは、何かを定義することと同じではありません。時には同時に両方を行うこともできますが、どちらかを宣言して定義する必要があります。

なぜですか?

標準ではそうだと言われていますが、なぜ標準でそう言いますか?

コンパイルとリンクの仕組みと関係があります。いくつかのソースファイル(a.cppb.cpp)といくつかのヘッダファイル(a.hb.h)を持っていれば、それらをコンパイルしたいと思うでしょう。一般に、すべてのソースファイルを個別にコンパイルしてa.ob.oにしてから、最後にリンクして最終的なプログラムを作成します。

は、私たちが持っていたと言う:

// a.h ========================= 
class A { static int n; }; 

// b.h ========================= 
class B { static int n; }; 

// a.cpp ======================= 
#include "a.h" 
#include "b.h" 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
#include "a.h" 
#include "b.h" 

int bar() { return A::n - B::n; } 

#includeは、本質的にちょうど含むファイル内の他のファイルを貼り付けていることを覚えておいてください。だから我々はa.cppをコンパイルするときに、すべてのコンパイラが見てb.cppは次のとおりです。

A::nB::nがファイルをに行くべきオブジェクト
// a.cpp ======================= 
class A { static int n; }; 
class B { static int n; }; 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
class A { static int n; }; 
class B { static int n; }; 

int bar() { return A::n - B::n; } 

a.oまたはb.oa.cppb.cppの両方で宣言されているため、コンパイラはどこに置くべきか分かりません。それを両方に入れると、それを2回定義することになり、コンパイラはどちらを使うべきかを知りません(その場合、リンカはあなたに '複数定義されたシンボル'エラーを与えます)。

だから私たちには定義が必要です。定義はそれを置くために、ファイルオブジェクトを教えてくれる。

// a.cpp ======================= 
#include "a.h" 
#include "b.h" 

int A::n = 0; // A::n goes in a.o 

int foo() { return A::n + B::n; } 

// b.cpp ======================= 
#include "a.h" 
#include "b.h" 

int B::n = 0; // B::n goes in b.o 

int bar() { return A::n - B::n; } 

それはあなたがb.cppa.cppまたは両方に両方入れている可能性があることを指摘しておきます。正確に1回定義されていれば問題ありません。

関連する問題