11

名前空間や静的変数の根本的な誤解があると思います。しかし、私はこのテストコードを試しました(手で入力し、タイプミスを許します)

test.h:

namespace test{
   static int testNum=5;
   void setNum(int value);
}

main.cpp:

#include <test.h>

int test::setNum(int value){
   testNum=value;
}

int main(){
    test::setNum(9);
    cout<<test::testNum;
}

これを実行すると、期待した9ではなく5の値が得られます。testNum変数のインスタンスが2つあるように見えますが、これはstaticが実行する必要があることとは正反対のようです。どういうわけか、これらの機能がJavaの同等のものと同一であると仮定するのを間違えたと思います...

また、testNumの宣言から静的なものを削除すると、testNumが複数回宣言されるというエラーが表示されますが、その理由を誰かが説明できますか?

ありがとうございました

4

3 に答える 3

34

まず、あなたの誤解は名前空間とはstaticの関係もありません。それはについてだけです。この回答の残りの部分でtestNumは、名前空間にあるという事実は無関係であるという理由だけで参照します。

また、おそらく関数と呼ばれる別のファイルがあり、関数test.cppを含みtest.h、定義していると仮定しますsetNum

名前空間スコープの変数または関数(つまり、クラスメンバーではない、または関数のローカルではない)が宣言されstaticている場合、それはエンティティの名前がそのファイルの内部にあることを意味します。正式には「内部リンク」があります。つまり、名前で参照したり、他のファイルからリンクしたりすることはできません(ポインターを介して間接的に参照したり、別の関数に引数として渡すことで参照できます)。ファイルが定義するstatic int testNumと、各ファイルには、他のすべてのファイルとは異なる、その名前の独自の内部変数がありtestNumます(実際、1つのファイルが持つ可能性がstatic int testnumあり、別のファイルが持つ可能性があり、static double testnum別のファイルが持つ可能性がありstatic char* testNumます。これらはすべて、各ファイルに対して別個で内部的なものになります)。そのような定義をヘッダーに入れると、ヘッダーを含むすべてのファイルに独自のがありtestNumます。

したがってstatic、ヘッダーの変数には、を含むすべてのファイルで呼び出される異なる変数があります。つまり、1つのファイルに設定し、それを使用する別のファイルで関数を呼び出すと、同じ名前の別の変数が参照されます。testNumtest.htestNumtestNum

このため、ヘッダーで非定数static変数を宣言することはほとんどの場合間違っています。

これがないと、を含むすべてのファイルに変数staticの定義がありますが、これは許可されていません。すべてのエンティティは、プログラムで1回だけ定義する必要があります。これを解決する方法は、ヘッダーで変数を宣言することですが、定義はしません。これは、コンパイラーに変数を指示することによって行います。testNumtest.hextern

extern int testNum;  // N.B. no "= 1" here

これは、コンパイラに「外部リンケージ」と呼ばれる変数があることを示しているtestNumため、コードが参照する場合、testNum常に同じ変数を意味します(すべてのファイルで異なるエンティティである内部リンクを持つ名前ではありません)。extern変数を宣言した後は、プログラムのどこかに正確に1つの定義が提供されていることを確認する責任があるため、正確に1つのファイル(つまり、複数のファイルに含まれるヘッダーではない)で定義します。

int testNum = 1;
于 2012-07-13T21:12:35.487 に答える
7

static名前空間スコープは誤称であり、使用しないでください。これは、静的と宣言されたエンティティが内部名バインディングを持っていることを意味します。つまり、他の変換ユニットの同じ名前は異なるエンティティを参照し、変数定義の場合は、各変換ユニットに変数の個別のインスタンスが存在することになります。寿命には影響しません。(名前空間スコープで宣言または定義されたすべての変数には静的な有効期間があります。)

static名前空間スコープでも非推奨です。使用しないでください。

ヘッダーでの変数の宣言に関しては、接頭辞として、 externではなく、を付けてくださいstatic。変数が宣言されexternていて、初期化がない場合、宣言は定義ではありません。もちろん、この場合、どこかに(単一のソースファイルで)定義を提供する必要があります。次のようなもの:

extern int testNum = 5;
int testNum = 5;
int testNum;          //  implicitly initialized with 0.

編集:

やや明確にするために:ここでは、ライフタイムと名前バインディングの間にいくつかの混乱があります:

  • オブジェクトには存続期間(自動、静的、動的、または一時的、または例外)があり、
  • 名前はエンティティにバインドされています。名前が変数であると宣言されている場合、エンティティはオブジェクトです。

キーワードを静的存続期間と混同しないでください。static(関数は可能ですがstatic、C ++では関数の有効期間は定義されていません。関数はそこにあります。)

これらに関する規則はあまり正統ではありません。基本的に、寿命に関して:

  • 名前空間スコープで宣言されたすべての変数には、常に静的な有効期間があります。
  • ローカルスコープで宣言された変数は、宣言されていない限り、自動存続期間がstaticあります。
  • クラススコープで宣言された変数は、宣言されていない限り、それらを含むクラスオブジェクトの存続期間がありますstatic。生涯に関して。

静的な存続期間を持つオブジェクトは、の前mainに発生し、から戻るまで存続しますmain

名前バインディングに関して:

  • 名前空間スコープで宣言された変数は、宣言されていない限り、外部の名前バインディングを持ちます。宣言されているstatic場合は、内部の名前バインディングがあります(ただし、この使用staticは非推奨です) 。constextern
  • クラススコープで宣言された変数は、宣言されている場合でも、外部の名前バインディングを持っていますstatic
  • ブロックスコープで宣言された変数にはバインディングがありません。

最後に、宣言が定義であるかどうかという問題があります。定義の場合、メモリが割り当てられ、オブジェクトが初期化されます(または初期化される可能性があります)。定義でない場合は、宣言で宣言されたエンティティ(オブジェクト)の定義がどこかにあることをコンパイラに通知するだけです。一般に、変数宣言は、宣言されていて初期化子がない場合を除いて、 定義ですextern

于 2012-07-13T20:58:09.503 に答える
2

何が悪いのかを尋ねる投稿をする前に、コードに実際に問題があることを確認することをお勧めします;)

私はあなたのタイプミスをコピー/貼り付けして修正し、手動でインクルードを行いました:

#include <iostream>
using namespace std;

namespace test{
   static int testNum=5;
   void setNum(int value);
}

void test::setNum(int value){
   testNum=value;
}

int main(){
    test::setNum(9);
    cout<<test::testNum;
}

結果:

$ ./a.out 
9

あなたが言っていないのは、あなたのプログラムに他に何があるかということです。main.cppだけでなく、test.hを含める場合は、各.cppファイルにtestNumの独自のコピーがあります。それらを共有したい場合は、1つを除いてすべてをマークする必要がありますextern

于 2012-07-13T21:00:04.890 に答える