質問を明確にするために、「静的」キーワードの使用法を次の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/