staticキーワードとC ++でのさまざまな使用法


195

キーワードstaticはC ++でいくつかの意味を持つものですが、私は非常に混乱し、実際にどのように機能するのかを気にすることはありません。

私が理解していることからstatic、ストレージ期間があります。これは、グローバルの場合、プログラムの存続期間中持続することを意味しますが、ローカルについて話すとき、それはデフォルトでゼロに初期化されることを意味します。

C ++標準は、キーワードを持つクラスデータメンバーに対してこれを述べていますstatic

3.7.1静的ストレージ期間[basic.stc.static]

3キーワードstaticを使用して、静的ストレージ期間を持つローカル変数を宣言できます。

4クラス定義のクラスデータメンバーにキーワードstaticを適用すると、データメンバーに静的ストレージ期間が与えられます。

ローカル変数とはどういう意味ですか?それは関数ローカル変数ですか?関数をローカルで宣言すると、関数staticは一度だけ初期化されるため、最初にこの関数に入る場合もあります。

また、クラスメンバーに関してストレージ期間についてのみ話します。インスタンス固有ではstaticないのはどうですか。それはまた、noのプロパティですか?それとも保管期間ですか?

ではstatic、ファイルスコープの場合はどうでしょうか。すべてのグローバル変数は、デフォルトで静的ストレージ期間があると見なされますか?以下(セクション3.7.1から)はそう示しているようです。

1動的ストレージ期間がなく、スレッドストレージ期間がなく、ローカルでないすべての変数は、静的ストレージ期間を持ちます。これらのエンティティのストレージは、プログラムの期間中継続します(3.6.2、3.6.3)。

static変数のリンケージにはどのように関係しますか?

この全体のstaticキーワードは、紛れも誰かがそれ英語のためのさまざまな用途を明確にしても私に言うことができ、混乱している時に初期化するためにstatic、クラスのメンバーを?


回答:


147

変数:

static変数、で定義されている翻訳単位の「存続期間」に対して存在します。

  • 名前空間スコープ内(つまり、関数やクラスの外)にある場合、他の翻訳単位からはアクセスできません。これは「内部リンケージ」または「静的ストレージ期間」として知られています。(これ以外のヘッダーではこれを行わないでくださいconstexpr。それ以外の場合、各翻訳単位に個別の変数が含まれ、混乱を招きます)
  • 関数内の変数の場合、他のローカル変数と同様に、関数の外部からアクセスすることはできません。(これは彼らが言及したローカルです)
  • クラスメンバーにはによる制限付きスコープはありませんstaticが、クラスやインスタンス(などstd::string::npos)からアドレス指定できます。[注:クラスで静的メンバーを宣言できますが、通常は翻訳単位(cppファイル)で定義する必要があるため、クラスごとに1つしかありません]

コードとしての場所:

static std::string namespaceScope = "Hello";
void foo() {
    static std::string functionScope= "World";
}
struct A {
   static std::string classScope = "!";
};

翻訳単位の関数が実行される前に(おそらくmain実行が開始された後)、その翻訳単位の静的ストレージ期間(名前空間スコープ)を持つ変数は「定数初期化」され(constexpr可能な場合はゼロ、そうでない場合はゼロ)、その後非ローカルは、翻訳単位で定義されている順序で適切「動的に初期化」さます (std::string="HI";そうでない場合などconstexpr)。最後に、関数ローカルstaticは、最初の実行が宣言された行に「到達」したときに初期化されます。すべてのstatic変数はすべて、初期化の逆の順序で破棄されます。

このすべてを正しく行うための最も簡単な方法は、constexpr初期化されていないすべての静的変数を関数の静的ローカルにすることです。これにより、すべての静的変数/グローバルが何に関係なく使用しようとしたときに正しく初期化され、静的な初期化を防ぐことができます。注文の大失敗

T& get_global() {
    static T global = initial_value();
    return global;
}

名前空間スコープ変数にデフォルトで「静的ストレージ期間」があると仕様に記載されている場合、これらは「翻訳単位の存続期間」ビットを意味しますが、ファイルの外部にアクセスできないことを意味しないため、注意してください。

関数

大幅に単純化staticされ、クラスメンバー関数としてよく使用され、自立関数に使用されることはほとんどありません。

静的メンバー関数は、クラスのインスタンスなしで呼び出すことができ、インスタンスがないため、クラスの非静的メンバーにアクセスできないという点で、通常のメンバー関数とは異なります。静的変数は、インスタンスメンバーを絶対に参照しないクラスの関数を作成する場合や、staticメンバー変数を管理する場合に便利です。

struct A {
    A() {++A_count;}
    A(const A&) {++A_count;}
    A(A&&) {++A_count;}
    ~A() {--A_count;}

    static int get_count() {return A_count;}
private:
    static int A_count;
}

int main() {
    A var;

    int c0 = var.get_count(); //some compilers give a warning, but it's ok.
    int c1 = A::get_count(); //normal way
}

staticフリー機能は、機能が他の翻訳単位で参照されないので、リンカーはそれを完全に無視することができることを意味します。これにはいくつかの目的があります。

  • 関数が他のファイルから使用されないことを保証するためにcppファイルで使用できます。
  • ヘッダーに入れることができ、すべてのファイルに関数の独自のコピーがあります。インラインはほとんど同じことを行うため、有用ではありません。
  • 作業を減らすことでリンク時間を短縮
  • 各翻訳単位に同じ名前の関数を置くことができ、それらはすべて異なることを実行できます。たとえばstatic void log(const char*) {}、各cppファイルにを配置すると、それぞれが異なる方法でログに記録できます。

1
クラスのメンバーはどうですか?3番目のケースではないですか?
エティエンヌ2013年

4
@Etienne-静的クラスのデータメンバーは、他の翻訳単位からアクセスできること、およびメンバー関数からのアクセスを除くすべてのアクセスでclassname::スコープを指定する必要があることを除いて、静的グローバル変数と同じです。静的クラスメンバー関数は、グローバル関数のようですが、スコープがクラスにあるか、通常のメンバーのようですが、ありthisません(それは選択肢ではありません-これら2つは同等でなければなりません)。
Steve314 2013年

1
@LuchianGrigore:私はあなたの要点を見ていますが、どの言葉遣いを使うべきかわかりません。
Mooing Duck 2013

1
@ Steve314:私はあなたの意味を理解していますが、あまりにもひどくオーバーロードされた用語をstaticとして扱うときは、私たち全員がもう少し注意したいのですが。特に、すべてのグローバル(実際には名前空間レベル)変数は静的期間を持っているため、静的グローバル変数静的を追加namespace A { static int x; }することは、内部リンケージを意味し、静的クラスデータメンバーの動作とは大きく異なると理解できます。
デビッド・ロドリゲス-ドリベス2013年

1
「名前空間スコープ内にある場合、他の翻訳単位からはアクセスできません...」名前空間スコープ内にあるとはどういう意味ですか?いつもそうとは限らないので、例と反例を挙げていただけますか?
AturSams 2015

66

静的ストレージ期間とは、プログラムの存続期間を通じて、変数がメモリ内の同じ場所に存在することを意味します。

リンケージはこれと直交しています。

これはあなたができる最も重要な区別だと思います。これと残りの部分を理解し、覚えておくと、簡単に理解できるはずです(@Tonyに直接対処するのではなく、将来これを読む可能性のある人なら誰でも)。

キーワードstaticは、内部リンケージ静的ストレージを示すために使用できますが、本質的には異なります。

ローカル変数とはどういう意味ですか?それは関数ローカル変数ですか?

はい。変数が初期化されるとき(関数の最初の呼び出し時と実行パスが宣言点に達するとき)に関係なく、変数はプログラムの存続期間中、メモリ内の同じ場所に存在します。この場合、static静的ストレージを提供します。

では、静的スコープとファイルスコープの場合はどうでしょうか。すべてのグローバル変数は、デフォルトで静的ストレージ期間があると見なされますか?

はい、すべてのグローバルには、定義上、静的な保存期間があります(これで、意味がわかりやすくなりました)。ただし、名前空間スコープ変数はで宣言されていませんstatic。これにより、内部リンケージが得られるため、翻訳単位ごとの変数になります。

staticは変数のリンケージとどのように関係しますか?

名前空間スコープの変数の内部リンケージを提供します。メンバーとローカル変数に静的ストレージ期間を提供します。

これをすべて拡張してみましょう:

//

static int x; //internal linkage
              //non-static storage - each translation unit will have its own copy of x
              //NOT A TRUE GLOBAL!

int y;        //static storage duration (can be used with extern)
              //actual global
              //external linkage
struct X
{
   static int x;     //static storage duration - shared between class instances 
};

void foo()
{
   static int x;     //static storage duration - shared between calls
}

この静的キーワード全体は実に混乱しています

間違いなく、あなたがそれに慣れていない限り。:)言語に新しいキーワードを追加しないようにするために、委員会はこのキーワードIMOをこの効果に再利用しました-混乱。これは、さまざまなことを表すために使用されます(おそらく、反対のことを言うかもしれません)。


1
簡単に説明しましょう。static int x名前空間スコープで言うと、静的ないストレージが提供されるということですか。
Michael Hagar

30

質問を明確にするために、「静的」キーワードの使用法を次の3つの異なる形式に分類します。

(A)。変数

(B)。関数

(C)。クラスのメンバー変数/関数

各小見出しについて、以下で説明します。

(A)変数の「静的」キーワード

これは少し難しいかもしれませんが、適切に説明して理解すれば、非常に簡単です。

これを説明するには、最初に変数のスコープ、期間、リンケージについて知ることが非常に役立ちます。これがないと、静的キーワードのあいまいな概念では常に物事を理解することは困難です。

1.スコープ:ファイル内のどこで変数にアクセスできるかを決定します。次の2つのタイプがあります。(i)ローカルスコープまたはブロックスコープ。(ii)グローバルスコープ

2.期間:変数が作成および破棄される時期を決定します。繰り返しますが、2つのタイプがあります。(i)自動ストレージ期間(LocalまたはBlockスコープを持つ変数の場合)。(ii)の静的ストレージ期間(グローバルスコープまたはコードブロックの機能や内のローカル変数()の変数に対する静的指定子)。

3.リンケージ:別のファイルで変数にアクセス(またはリンク)できるかどうかを決定します。繰り返しますが(幸運にも)2つのタイプがあります:(i)内部リンケージ (ブロックスコープとグローバルスコープ/ファイルスコープ/グローバル名前空間スコープを持つ変数の場合)(ii)外部リンケージ(グローバルスコープ/ファイルスコープ/グローバル名前空間スコープ)

単純なグローバル変数とローカル変数を理解するために、以下の例を参照してください(静的ストレージ期間のローカル変数はありません)。

//main file
#include <iostream>

int global_var1; //has global scope
const global_var2(1.618); //has global scope

int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is 
//  executed and destroyed, when main goes out of scope
 int local_var1(23);
 const double local_var2(3.14);

 {
/* this is yet another block, all variables declared within this block are 
 have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e, 
/*they are created at the point of definition within this block,
 and destroyed as soon as this block ends */
   char block_char1;
   int local_var1(32) //NOTE: this has been re-declared within the block, 
//it shadows the local_var1 declared outside

 std::cout << local_var1 <<"\n"; //prints 32

  }//end of block
  //local_var1 declared inside goes out of scope

 std::cout << local_var1 << "\n"; //prints 23

 global_var1 = 29; //global_var1 has been declared outside main (global scope)
 std::cout << global_var1 << "\n"; //prints 29
 std::cout << global_var2 << "\n"; //prints 1.618

 return 0;
}  //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates 
//(in this case program ends with end of main, so both local and global
//variable go out of scope together

リンケージのコンセプトです。あるファイルで定義されたグローバル変数を別のファイルで使用する場合、変数のリンケージが重要な役割を果たします。

グローバル変数のリンケージは、キーワードによって指定されます:(i)static、および(ii)extern

(これで説明が表示されます)

staticキーワードは、ローカルスコープとグローバルスコープを持つ変数に適用できます。どちらの場合も、意味は異なります。最初に、グローバルスコープを持つ変数での「静的」キーワードの使用法を説明し(キーワード 'extern'の使用法も明確にする)、後でローカルスコープを持つ変数の使用法を説明します。

1.グローバルスコープを持つ変数の静的キーワード

グローバル変数は静的な継続時間を持っています。つまり、グローバル変数は、それが使用されているコードの特定のブロック(たとえばmain())が終了してもスコープ外になりません。リンケージに応じて、それらが宣言されているのと同じファイル内でのみ(静的グローバル変数の場合)、または宣言されているファイルの外でもファイルの外部(外部型グローバル変数)にアクセスできます。

extern指定子を持つグローバル変数の場合、この変数が初期化されたファイルの外部でアクセスされる場合、関数が転送される必要があるのと同じように、それが使用されているファイルで変数が転送宣言される必要があります。定義が使用されている場所とは異なるファイルにある場合に宣言されます。

対照的に、グローバル変数に静的キーワードがある場合、それが宣言されている外部のファイルでは使用できません。

(明確にするために、以下の例を参照してください)

例えば:

//main2.cpp
 static int global_var3 = 23;  /*static global variable, cannot be                            
                                accessed in anyother file */
 extern double global_var4 = 71; /*can be accessed outside this file                  linked to main2.cpp */
 int main() { return 0; }

main3.cpp

//main3.cpp
#include <iostream>

int main()
{
   extern int gloabl_var4; /*this variable refers to the gloabal_var4
                            defined in the main2.cpp file */
  std::cout << global_var4 << "\n"; //prints 71;

  return 0;
}

現在、c ++の変数はconstまたはnon-constのいずれかであり、「const-ness」ごとに、何も指定されていない場合のデフォルトのc ++リンケージの2つのケースが得られます。

(i)グローバル変数が非constの場合、そのリンケージはデフォルトexternです。つまり、非constグローバル変数は、externキーワードを使用した前方宣言によって別の.cppファイルでアクセスできます(つまり、非constグローバル)変数には外部リンケージがあります(もちろん静的な期間)。また、定義されている元のファイルでのexternキーワードの使用は冗長です。この場合、const以外のグローバル変数を外部ファイルにアクセスできないようにするには、変数のタイプの前に指定子 'static'を使用します

(ii)グローバル変数がconstの場合、そのリンケージはデフォルト静的です。つまり、constグローバル変数は、それが定義されている場所以外のファイルではアクセスできません(つまり、constグローバル変数には内部リンケージがあります(静的期間)もちろん))。また、constグローバル変数が別のファイルでアクセスされるのを防ぐためのstaticキーワードの使用は冗長です。ここで、constグローバル変数に外部リンケージを持たせるには、変数のタイプの前に指定子 'extern'を使用します

これは、さまざまなリンケージを持つグローバルスコープ変数の概要です。

//globalVariables1.cpp 

// defining uninitialized vairbles
int globalVar1; //  uninitialized global variable with external linkage 
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared

次に、上記のグローバル変数が別のファイルでアクセスされた場合の動作を調査します。

//using_globalVariables1.cpp (eg for the usage of global variables above)

// Forward declaration via extern keyword:
 extern int globalVar1; // correct since globalVar1 is not a const or static
 extern int globalVar2; //incorrect since globalVar2 has internal linkage
 extern const int globalVar4; /* incorrect since globalVar4 has no extern 
                         specifier, limited to internal linkage by
                         default (static specifier for const variables) */
 extern const double globalVar5; /*correct since in the previous file, it 
                           has extern specifier, no need to initialize the
                       const variable here, since it has already been
                       legitimately defined perviously */

2.ローカルスコープを持つ変数の静的キーワード

ローカルスコープの変数の静的キーワードの更新(2019年8月)

これはさらに2つのカテゴリに分類できます。

(i)ファンクションブロック内の変数の静的キーワード、および(ii)名前のないローカルブロック内の変数の静的キーワード。

(i)関数ブロック内の変数の静的キーワード。

以前、ローカルスコープの変数には自動継続時間があること、つまり、ブロックに入ると(通常のブロックであっても、関数ブロックであっても)存在し、ブロックが終了すると存在しなくなる、長いストーリー、変数があると述べましたローカルスコープでは自動継続時間があり、自動継続時間変数(およびオブジェクト)にはリンケージがありません。つまり、コードブロックの外には表示されません。

静的指定子が関数ブロック内のローカル変数に適用される場合、それは変数の持続時間を自動から静的に変更し、その寿命はプログラムの全持続時間です。つまり、固定されたメモリ位置を持ち、その値のみが初期化されますcppリファレンスに記載されているように、プログラムの起動前に1回(初期化を割り当てと混同しないでください)

例を見てみましょう。

//localVarDemo1.cpp    
 int localNextID()
{
  int tempID = 1;  //tempID created here
  return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here :-)


int main()
{
  int employeeID1 = localNextID();  //employeeID1 = 1
  int employeeID2 = localNextID();  // employeeID2 = 1 again (not desired)
  int employeeID3 = newNextID(); //employeeID3 = 0;
  int employeeID4 = newNextID(); //employeeID4 = 1;
  int employeeID5 = newNextID(); //employeeID5 = 2;
  return 0;
}

静的ローカル変数と静的グローバル変数の上記の基準を見ると、それらの違いが何であるかを尋ねたくなるかもしれません。グローバル変数はコード内の任意の時点で(const -nessとextern -nessに応じて異なる変換単位と同様に)アクセスできますが、関数ブロック内で定義された静的変数には直接アクセスできません。変数は、関数値または参照によって返される必要があります。例でこれを示しましょう:

//localVarDemo2.cpp 

//static storage duration with global scope 
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here


int main()
{
    //since globalId is accessible we use it directly
  const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
  const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;

  //const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly. 
  int employeeID2 = newNextID(); //employeeID3 = 0;
  int employeeID2 = newNextID(); //employeeID3 = 1;

  return 0;
}

静的グローバル変数と静的ローカル変数の選択に関する詳細な説明は、このstackoverflowスレッドにあります。

(ii)名前のないローカルブロック内の変数の静的キーワード。

ローカルブロックがスコープ外になると、(関数ブロックではなく)ローカルブロック内の静的変数にブロックの外部からアクセスできなくなります。このルールに対する警告はありません。

    //localVarDemo3.cpp 
    int main()
    {

      {
          const static int static_local_scoped_variable {99};
      }//static_local_scoped_variable goes out of scope

      //the line below causes compilation error
      //do_something is an arbitrary function
      do_something(static_local_scoped_variable);
      return 0;
    }

C ++ 11は、constexprコンパイル時に式の評価を保証し、コンパイラーがコードを最適化できるようにするキーワードを導入しました。スコープ内の静的const変数の値がコンパイル時にわかっている場合、コードはを使用した場合と同様の方法で最適化されconstexprます。ここに小さな例があります

また、このStackoverflowスレッドの変数constexprとの違いを調べることもお勧めします。これで、変数に適用されるstaticキーワードの説明は終わりです。static const

B.関数に使用される 'static'キーワード

関数の観点から見ると、staticキーワードの意味は簡単です。ここでは、関数のリンケージを指します。 通常、cppファイル内で宣言されたすべての関数にはデフォルトで外部リンケージがあります。つまり、あるファイルで定義された関数は、前方宣言によって別のcppファイルで使用できます。

関数宣言の前に静的キーワードを使用すると、そのリンケージがinternalに制限されます。つまり、静的関数は、その定義外のファイル内では使用できません。

C.クラスのメンバー変数と関数に使用されるStaitcキーワード

1.クラスのメンバー変数の「静的」キーワード

私はここの例から直接始めます

#include <iostream>

class DesignNumber
{
  private:

      static int m_designNum;  //design number
      int m_iteration;     // number of iterations performed for the design

  public:
    DesignNumber() {     }  //default constructor

   int  getItrNum() //get the iteration number of design
   {
      m_iteration = m_designNum++;
      return m_iteration;
   }
     static int m_anyNumber;  //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
                     // note : no need of static keyword here
                     //causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public 
                                    static member  */
enter code here

int main()
{
   DesignNumber firstDesign, secondDesign, thirdDesign;
   std::cout << firstDesign.getItrNum() << "\n";  //prints 0
   std::cout << secondDesign.getItrNum() << "\n"; //prints 1
   std::cout << thirdDesign.getItrNum() << "\n";  //prints 2

   std::cout << DesignNumber::m_anyNumber++ << "\n";  /* no object
                                        associated with m_anyNumber */
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101

   return 0;
}

この例では、静的変数m_designNumはその値を保持し、この1つのプライベートメンバー変数(静的であるため)は、オブジェクトタイプDesignNumberのすべての変数と共有されます。

また、他のメンバー変数と同様に、クラスの静的メンバー変数はクラスオブジェクトに関連付けられていません。これは、メイン関数でのanyNumberの出力によって示されています

クラスのconst vs非const静的メンバー変数

(i)非constクラスの静的メンバー変数 前の例では、静的メンバー(パブリックとプライベートの両方)は非定数でした。ISO標準では、非const静的メンバーをクラスで初期化することを禁止しています。したがって、前の例と同様に、クラス定義の後にそれらを初期化する必要があります。ただし、staticキーワードを省略する必要があることに注意してください。

(ⅱ)クラスのconstの静的メンバ変数を 、これは簡単ですし、他のconstメンバ変数の初期化の大会で行く、クラスのつまりconstの静的メンバ変数をすることができ、宣言の時点で初期化され、彼らが最後に初期化することができますクラス定義の後に初期化されるときに、キーワードconstが静的メンバーに追加される必要があるという1つの注意点を持つクラス宣言の。

ただし、宣言時にconst静的メンバー変数を初期化することをお勧めします。これは標準のC ++規則に準拠しており、コードをきれいに見せます

クラスの静的メンバー変数のその他の例については、learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/から次のリンクを 参照してください。

2.クラスのメンバー関数の「静的」キーワード

クラスのメンバー変数が静的であるように、クラスのメンバー関数も静的であることができます。クラスの通常のメンバー関数は、常にクラス型のオブジェクトに関連付けられています。対照的に、クラスの静的メンバー関数はクラスのオブジェクトに関連付けられていません。つまり、* thisポインターはありません。

次に、クラスの静的メンバー関数には* thisポインターがないため、メイン関数(ClassName :: functionName();)でクラス名とスコープ解決演算子を使用して呼び出すことができます。

3番目に、クラスの非静的メンバー変数はクラスオブジェクトに属している必要があるため、クラスの静的メンバー関数はクラスの静的メンバー変数にのみアクセスできます。

クラスの静的メンバー関数のその他の例については、learncpp.comから次のリンクを参照してください。

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/


1
1)c ++ 17より前struct Foo{static const std::string name = "cpp";};は、クラス内で初期化できるのは、整数の静的constメンバー変数のみです。たとえば、エラーの場合name、クラスの外部で定義する必要があります。c ++ 17で導入されたインライン変数を使用すると、次のようにコーディングできますstruct Foo{static inline const std::string name = "cpp";};。2)パブリック静的メンバー/メンバー関数は、スコープ解決演算子を使用したクラス名およびドット演算子を使用したインスタンスによってアクセスできます(例:instance.some_static_method())
oz1

「m_anyVariable」を「m_anyNumber」にしてはいけませんか?あなたの最後のコード例では?
gebbissimo 2018

答えの完全性と正しさを判断することはできませんが、それは本当に包括的で、簡単に理解できました。どうもありがとう!改善したい場合は、冒頭の短い要約が非常に長いテキストであり、主なポイントは「内部/外部リンケージ」
gebbissimo 2018

18

それは実際には非常に簡単です。関数のスコープ内で変数を静的として宣言すると、その値はその関数への連続した呼び出し間で保持されます。そう:

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

増分された値を記憶しているため、678ではなくが表示され666ます。

静的メンバーについては、クラスのインスタンス間で値を保持します。したがって、次のコード:

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

first.aとsecond.aは基本的に同じ変数なので、4を出力します。初期化についてはこちらの質問をご覧ください


これは名前空間スコープ変数を扱いません。
Michael Hagar

10

staticファイルスコープで変数を宣言すると、その変数はその特定のファイルでのみ使用できます(技術的には、* translationユニットですが、あまり複雑にしないでください)。例えば:

a.cpp

static int x = 7;

void printax()
{
    cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;

void printbx()
{
    cout << "from b.cpp: x=" << x << endl;
}

main.cpp:

int main(int, char **)
{
    printax(); // Will print 7
    printbx(); // Will print 9

    return 0;
}

以下のためのローカル変数、static変数はゼロで初期化されることを意味し、呼び出しの間その値を保持します。

unsigned int powersoftwo()
{
    static unsigned lastpow;

    if(lastpow == 0)
        lastpow = 1;
    else
        lastpow *= 2;

    return lastpow;
}

int main(int, char **)
{
    for(int i = 0; i != 10; i++)
        cout << "2^" << i << " = " << powersoftwo() << endl;
}

クラス変数の場合、そのクラスのすべてのメンバー間で共有されるのは、その変数の単一のインスタンスのみであることを意味します。権限に応じて、クラスの外部から完全修飾名を使用して変数にアクセスできます。

class Test
{
private:
    static char *xxx;

public:
    static int yyy;

public:
    Test()
    {        
        cout << this << "The static class variable xxx is at address "
             << static_cast<void *>(xxx) << endl;
        cout << this << "The static class variable yyy is at address "
             << static_cast<void *>(&y) << endl;
    }
};

// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;

int main(int, char **)
{
    Test t1;
    Test t2;

    Test::yyy = 666;

    Test t3;
};

非クラス関数にマークを付けると、staticそのファイルからのみ関数にアクセスでき、他のファイルからはアクセスできなくなります。

a.cpp

static void printfilename()
{ // this is the printfilename from a.cpp - 
  // it can't be accessed from any other file
    cout << "this is a.cpp" << endl;
}

b.cpp

static void printfilename()
{ // this is the printfilename from b.cpp - 
  // it can't be accessed from any other file
    cout << "this is b.cpp" << endl;
}

クラスメンバー関数の場合、それらをマークするstaticと、オブジェクトの特定のインスタンスで関数を呼び出す必要がない(つまり、thisポインターがない)ことを意味します。

class Test
{
private:
    static int count;

public:
    static int GetTestCount()
    {
        return count;
    };

    Test()
    {
        cout << this << "Created an instance of Test" << endl;
        count++;
    }

    ~Test()
    {
        cout << this << "Destroyed an instance of Test" << endl;
        count--;
    }
};

int Test::count = 0;

int main(int, char **)
{
    Test *arr[10] = { NULL };

    for(int i = 0; i != 10; i++)
        arr[i] = new Test();

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    // now, delete them all except the first and last!
    for(int i = 1; i != 9; i++)
        delete arr[i];        

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[0];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[9];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    return 0;
}

8

静的変数は、各クラスが独自の変数を持つのではなく、クラスのすべてのインスタンス間で共有されます。

class MyClass
{
    public:
    int myVar; 
    static int myStaticVar;
};

//Static member variables must be initialized. Unless you're using C++11, or it's an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;

MyClass classA;
MyClass classB;

「MyClass」の各インスタンスには独自の「myVar」がありますが、同じ「myStaticVar」を共有しています。実際、 'myStaticVar'にアクセスするためにMyClassのインスタンスさえ必要とせず、次のようにクラスの外でアクセスできます。

MyClass::myStaticVar //Assuming it's publicly accessible.

関数内でローカル変数として(クラスメンバー変数としてではなく)使用される場合、静的キーワードは別のことを行います。グローバルスコープを指定せずに、永続変数を作成できます。

int myFunc()
{
   int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
   myVar++;

   //Given the above code, this will *always* print '1'.
   std::cout << myVar << std::endl;

   //The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
   static int myStaticVar = 0;

   //Each time the code reaches here, myStaticVar is incremented.
   myStaticVar++;

   //This will print a continuously incrementing number,
   //each time the function is called. '1', '2', '3', etc...
   std::cout << myStaticVar << std::endl;
}

永続性の観点からはグローバル変数ですが、スコープ/アクセシビリティがグローバルではありません。

静的メンバー関数を持つこともできます。静的関数は基本的に非メンバー関数ですが、クラス名の名前空間内にあり、クラスのメンバーへのプライベートアクセス権があります。

class MyClass
{
    public:
    int Func()
    {
        //...do something...
    }

    static int StaticFunc()
    {
        //...do something...
    }
};

int main()
{
   MyClass myClassA;
   myClassA.Func(); //Calls 'Func'.
   myClassA.StaticFunc(); //Calls 'StaticFunc'.

   MyClass::StaticFunc(); //Calls 'StaticFunc'.
   MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!

   return 0;
}

メンバー関数を呼び出すと、「this」と呼ばれる隠しパラメーターがあります。これは、関数を呼び出すクラスのインスタンスへのポインターです。静的メンバー関数にその隠しパラメーターがありません ...クラスインスタンスなしで呼び出すことができますが、クラスの非静的メンバー変数にはアクセスできません。特定のクラスインスタンスでは呼び出されていません。


1
「それが公にアクセス可能であると仮定します。」- そうではありません。
Luchian Grigore 2013年

2
myStaticVarまた、定義する必要があります。staticキーワードのセマンティクスについての質問に答えるとき、あなたはそう思いませんか?
Praetorian

@プレトリアン:ありがとう、修正しました。
Jamin Grey

1
@JaminGrey「静的スタンドアロン」とは、静的な非メンバー関数を意味し、現在のCPPファイルでのみ新しい機能が必要な場合はいつでも記述し、リンカーが追加のシンボルを処理する必要がないようにします。
VR

1
@VR奇数!機能が存在することを知りませんでした。私の知識を広げてくれてありがとう!
Jamin Grey 2016

1

私はCプログラマではないので、Cプログラムでのstaticの使用に関する情報を適切に提供することはできませんが、オブジェクト指向プログラミングに関して、staticは基本的に変数、関数、またはクラスを同じと宣言しますプログラムの全期間を通じて。例を見てみましょう。

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
private:
    void somePrivateMethod();
};

Mainでこのクラスをインスタンス化すると、次のようになります。

int main()
{
   A a1;
   //do something on a1
   A a2;
   //do something on a2
}

これらの2つのクラスインスタンスは互いに完全に異なり、互いに独立して動作します。しかし、このようにクラスAを再作成するとします。

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
    static int x;
private:
    void somePrivateMethod();
};

再びメインに戻りましょう。

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   A a2;
   a2.x++;
   //do something on a2
}

次に、a1とa2はint xの同じコピーを共有します。これにより、a1のxに対する操作は、a2のxの操作に直接影響します。だから私がこれをするなら

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   cout << a1.x << endl; //this would be 1
   A a2;
   a2.x++;
   cout << a2.x << endl; //this would be 2 
   //do something on a2
}

クラスAの両方のインスタンスは、静的変数と関数を共有します。これがあなたの質問に答えることを願っています。Cに関する私の限られた知識では、関数または変数を静的として定義することは、関数または変数が静的として定義されているファイルにのみ可視であることを意味します。C ++は、Cと完全に下位互換性があるため、CとC ++の両方の方法で変数を静的として宣言できます。


1

ローカル変数とはどういう意味ですか?それは関数ローカル変数ですか?

はい-関数のローカル変数などの非グローバル。

関数をローカルとしてstaticとして宣言すると、初期化が1回だけであることもあるので、最初にこの関数に入ったとき。

正しい。

また、クラスメンバーに関するストレージ期間についてのみ説明します。インスタンス固有ではないのはどうですか。これは静的noのプロパティでもありますか?それとも保管期間ですか?

class R { static int a; }; // << static lives for the duration of the program

言うことであること、のすべてのインスタンスRシェアはint R::a- int R::aコピーされることはありません。

では、静的スコープとファイルスコープの場合はどうでしょうか。

事実上、必要に応じてコンストラクタ/デストラクタを持つグローバル-初期化はアクセスするまで延期されません。

staticは変数のリンケージとどのように関係しますか?

ローカル関数の場合、それは外部関数です。アクセス:関数にアクセスできます(もちろん、それを返さない限り)。

クラスの場合、それは外部です。アクセス:標準のアクセス指定子が適用されます(パブリック、保護、プライベート)。

static 宣言されている場所(ファイル/名前空間)に応じて、内部リンケージを指定することもできます。

この静的キーワード全体は実に混乱しています

C ++では目的が多すぎます。

誰かがそれの英語の異なる使用法を明確にし、静的クラスメンバーをいつ初期化するかを教えてもらえますか?

mainロードされ、コンストラクターがある場合は、自動的に初期化されます。それは良いことのように思えるかもしれませんが、初期化の順序は主に制御できないため、複雑な初期化を維持することは非常に難しくなるため、これを最小限に抑える必要があります。プロジェクト。静的な保存期間を持つデータに関する限り、特に可変(グローバル変数)の場合は、この設計を最小限に抑えるようにしてください。初期化の「時間」もさまざまな理由で変化します。ローダーとカーネルには、問題のデータに応じて、メモリフットプリントを最小化し、初期化を遅らせるいくつかのトリックがあります。


1

静的オブジェクト: staticキーワードを使用してクラスメンバーを静的に定義できます。クラスのメンバーを静的として宣言すると、クラスのオブジェクトがいくつ作成されても、静的メンバーのコピーは1つだけになります。

静的メンバーは、クラスのすべてのオブジェクトによって共有されます。他の初期化が存在しない場合、最初のオブジェクトが作成されると、すべての静的データはゼロに初期化されます。クラス定義に含めることはできませんが、次の例のように、スコープ解決演算子::を使用して静的変数を再宣言し、それが属するクラスを識別することで、クラスの外部で初期化できます。

静的データメンバーの概念を理解するために、次の例を試してみましょう。

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects.
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます。

Constructor called.
Constructor called.
Total objects: 2

静的関数メンバー: 関数メンバーを静的として宣言することで、クラスの特定のオブジェクトから独立させることができます。クラスのオブジェクトが存在せず、クラス名とスコープ解決演算子::のみを使用して静的関数にアクセスする場合でも、静的メンバー関数を呼び出すことができます。

静的メンバー関数は、クラスの外部から静的データメンバー、他の静的メンバー関数、および他の関数にのみアクセスできます。

静的メンバー関数にはクラススコープがあり、クラスのthisポインターにアクセスできません。静的メンバー関数を使用して、クラスの一部のオブジェクトが作成されたかどうかを判断できます。

静的関数メンバーの概念を理解するために、次の例を試してみましょう。

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{

   // Print total number of objects before creating object.
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

上記のコードをコンパイルして実行すると、次の結果が生成されます。

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.