Staticキーワードについて

7335 ワード


C++のstaticには,プロセスプログラミングにおけるstaticとオブジェクトプログラミングにおけるstaticの2つの使い方がある.前者は普通の変数と関数に適用され、クラスには関与しない.後者は主にstaticのクラスにおける役割を説明する.
一、プロセス設計におけるstatic向け
1、静的グローバル変数
グローバル変数の前にキーワードstaticを付けると、この変数は静的グローバル変数として定義されます.静的グローバル変数の例を次に示します.
//Example 1
#include <iostream.h>
void fn();
static int n; //        
void main()
{
	n=20;
	cout<<n<<endl;
	fn();
}

void fn()
{
	n++;
	cout<<n<<endl;
}
静的グローバル変数には、次のような特徴があります.
  • この変数は、グローバルデータ領域にメモリを割り当てる.
  • 初期化されていない静的グローバル変数は、プログラムによって自動的に0に初期化される(自動変数の値は、明示的に初期化されない限りランダムである);
  • 静的グローバル変数は、ファイル全体が表示され、ファイルの外では表示されないと宣言します. 

  • 静的変数は、後述する静的ローカル変数を含むグローバルデータ領域にメモリを割り当てます.完全なプログラムの場合、メモリ内の分散は次の図のようになります.
     
    コード領域
    グローバルデータ領域
    スタックゾーン
    スタック領域
    一般プログラムのnewによって生成された動的データはスタック領域に格納され,関数内部の自動変数はスタック領域に格納される.自動変数は一般に関数の終了に伴って空間を解放し、静的データ(関数内部の静的局所変数であっても)はグローバルデータ領域に格納される.グローバルデータ領域のデータは関数の終了によって空間を解放するわけではない.注意深い読者はExample 1のコードに
    	static int n; //        
    から
    	int n; //      
    プログラムは正常に動作します.
    確かに、グローバル変数を定義すると、ファイル内の変数の共有が可能になりますが、静的グローバル変数を定義するには、次のメリットがあります.
  • 静的グローバル変数は他のファイルでは使用できません.
  • 他のファイルでは同じ名前の変数を定義でき、衝突は発生しません.

  • 上記のサンプルコードを次のように変更できます.
    //Example 2
    //File1
    #include <iostream.h>
    void fn();
    static int n; //        
    void main()
    {
    	n=20;
    	cout<<n<<endl;
    	fn();
    }
    
    //File2
    #include <iostream.h>
    extern int n;
    void fn()
    {
    	n++;
    	cout<<n<<endl;
    }
    
    Example 2をコンパイルして実行すると、上記のコードはそれぞれコンパイルできますが、実行時にエラーが発生します.試しに
    static int n; //        
    
    から
    int n; //      
    
    実行プログラムを再コンパイルし、グローバル変数と静的グローバル変数の違いを注意深く理解します.
    2、静的局所変数
    ローカル変数の前にキーワードstaticを付けると、この変数は静的ローカル変数として定義されます.
    静的局所変数の例を次に示します.
    //Example 3
    #include <iostream.h>
    void fn();
    void main()
    {
    	fn();
    	fn();
    	fn();
    }
    void fn()
    {
    	static n=10;
    	cout<<n<<endl;
    	n++;
    }
    
    は、通常、プログラムが文を実行するたびにローカル変数にスタックメモリを割り当てる変数を関数内で定義します.しかし、プログラムが関数体を終了するにつれて、システムはスタックメモリを回収し、ローカル変数もそれに応じて失効します.
    しかし、2回の呼び出しの間に変数の値を保存する必要がある場合があります.一般的な考え方は、グローバル変数を定義して実現することです.しかし,これにより変数は関数そのものに属さず,関数のみの制御を受けず,プログラムのメンテナンスに不便をもたらす.
    静的局所変数はちょうどこの問題を解決できる.静的ローカル変数は、スタックに保存するのではなく、グローバルデータ領域に保存され、次回の値が付与されるまで、毎回の値は次の呼び出しに維持されます.
    静的ローカル変数には、次のような特徴があります.
  • この変数は、グローバルデータ領域にメモリを割り当てる.
  • 静的ローカル変数は、プログラムがオブジェクトの宣言を実行するときに最初に初期化され、すなわち、以降の関数呼び出しが初期化されなくなる.
  • 静的局所変数は一般に宣言で初期化され、明示的な初期化がなければプログラムによって自動的に0に初期化される.
  • プログラムの実行が終了するまで、グローバルデータ領域に常に存在します.しかし、その役割ドメインは局所的な役割ドメインであり、定義された関数または文ブロックが終了すると、その役割ドメインは終了する.

  • 3、静的関数
    関数の戻りタイプにstaticキーを付けると、関数は静的関数として定義されます.静的関数は通常の関数とは異なり、宣言されたファイルでのみ表示され、他のファイルでは使用できません.
    静的関数の例:
    //Example 4
    #include <iostream.h>
    static void fn();//      
    void main()
    {
    	fn();
    }
    void fn()//      
    {
    	int n=10;
    	cout<<n<<endl;
    }
    
    静的関数の利点を定義します.
  • 静的関数は他のファイルでは使用できません.
  • 他のファイルで同じ名前の関数を定義でき、衝突は発生しません.

  • 二、オブジェクト向けstaticキーワード(クラス内のstaticキーワード)
    1、静的データメンバー
    クラス内のデータ・メンバーの宣言にキーワードstaticを追加します.このデータ・メンバーは、クラス内の静的データ・メンバーです.まず、静的データ・メンバーの例を挙げます.
    //Example 5
    #include <iostream.h>
    class Myclass
    {
    public:
    	Myclass(int a,int b,int c);
    	void GetSum();
    private:
    	int a,b,c;
    	static int Sum;//        
    };
    int Myclass::Sum=0;//            
    
    Myclass::Myclass(int a,int b,int c)
    {
    	this->a=a;
    	this->b=b;
    	this->c=c;
    	Sum+=a+b+c;
    }
    
    void Myclass::GetSum()
    {
    	cout<<"Sum="<<Sum<<endl;
    }
    
    void main()
    {
    	Myclass M(1,2,3);
    	M.GetSum();
    	Myclass N(4,5,6);
    	N.GetSum();
    	M.GetSum();
    
    }
    
    では、静的データ・メンバーには次のような特徴があります.
  • 非静的データ・メンバーの場合、各クラス・オブジェクトには独自のコピーがあります.静的データ・メンバーはクラスのメンバーとして扱われます.このクラスのオブジェクトが何個定義されているかにかかわらず、静的データ・メンバーはプログラム内でコピーが1部しかなく、このタイプのすべてのオブジェクトが共有してアクセスします.すなわち、静的データメンバーは、クラスのすべてのオブジェクトに共通しています.このクラスの複数のオブジェクトでは、静的データ・メンバーにはメモリが1回しか割り当てられず、すべてのオブジェクトが共有されます.したがって、静的データメンバーの値は各オブジェクトに対して同じであり、その値は更新することができる.
  • 静的データ・メンバーは、グローバル・データ領域に格納される.静的データ・メンバー定義ではスペースを割り当てるため、クラス宣言では定義できません.Example 5では、文int Myclass::Sum=0;は、静的データ・メンバーを定義します.
  • 静的データ・メンバーは、一般的なデータ・メンバーと同様にpublic、protected、privateアクセス・ルールに従う.
  • 静的データメンバーは、グローバルデータ領域にメモリを割り当て、本クラスのすべてのオブジェクト共有に属するため、特定のクラスオブジェクトには属しず、クラスオブジェクトが生成されていない場合にその役割ドメインが表示されます.すなわち、クラスのインスタンスが生成されていない場合に操作できます.
  • 静的データ・メンバーの初期化は、一般的なデータ・メンバーの初期化とは異なります.静的データ・メンバーの初期化のフォーマットは、<データ型><クラス名>:<静的データ・メンバー名>=<値>
  • である.
  • クラスの静的データメンバーには、<クラスオブジェクト名>.静的データ・メンバー名>または<クラス・タイプ名>:<静的データ・メンバー名>静的データ・メンバーのアクセス権限が許可されている場合(すなわちpublicのメンバー)、プログラムにおいて上記の形式で静的データ・メンバーを参照することができる.
  • 静的データ・メンバーは、主に各オブジェクトに同じ属性がある場合に使用されます.たとえば、預金クラスでは、インスタンスごとに利息が同じです.したがって、利息を預金クラスの静的データメンバーに設定する必要があります.これには2つの利点があります.1つ目は、複数の預金クラスオブジェクトを定義しても、利息データ・メンバーがグローバル・データ領域に割り当てられたメモリを共有するため、記憶領域を節約することです.第二に、利息が変更される必要がある場合、一度変更すれば、すべての預金類の対象の利息がすべて変更されます.
  • グローバル変数と比較して、静的データ・メンバーの使用には、
  • という2つの利点があります.
  • 静的データメンバーはプログラムのグローバル名前空間に入っていないため、プログラム内の他のグローバル名前と衝突する可能性はない.
  • は、情報隠蔽を実現することができる.静的データ・メンバーはprivateメンバーであってもよく、グローバル変数はできません.

  • 2、静的メンバー関数
    静的データ・メンバーと同様に、クラスの特定のオブジェクトにサービスするのではなく、クラスのすべてのサービスに対する静的メンバー関数を作成することもできます.静的メンバー関数は、静的データメンバーと同様にクラスの内部実装であり、クラス定義の一部に属します.通常のメンバー関数には、通常のメンバー関数が常にクラスの特定のオブジェクトに属するため、thisポインタが隠されています.通常、thisはデフォルトです.関数fn()のように実際にはthis->fn()です.しかし、通常の関数と比較すると、静的メンバー関数は任意のオブジェクトに関連付けられていないため、thisポインタはありません.この意味では、クラスオブジェクトに属する非静的データ・メンバーにも非静的メンバー関数にもアクセスできません.残りの静的メンバー関数のみを呼び出すことができます.次に、静的メンバー関数の例を示します.
    //Example 6
    #include <iostream.h>
    class Myclass
    {
    public:
    	Myclass(int a,int b,int c);
    	static void GetSum();/        
    private:
    	int a,b,c;
    	static int Sum;//        
    };
    int Myclass::Sum=0;//            
    
    Myclass::Myclass(int a,int b,int c)
    {
    	this->a=a;
    	this->b=b;
    	this->c=c;
    	Sum+=a+b+c; //                 
    }
    
    void Myclass::GetSum() //         
    {
    //	cout<<a<<endl; //    ,a        
    	cout<<"Sum="<<Sum<<endl;
    }
    
    void main()
    {
    	Myclass M(1,2,3);
    	M.GetSum();
    	Myclass N(4,5,6);
    	N.GetSum();
    	Myclass::GetSum();
    }
    

    静的メンバー関数については、次の点にまとめることができます.
  • クラス外に現れる関数定義はキーワードstaticを指定できない.
  • 静的メンバー間で相互にアクセスすることができ、静的メンバー関数から静的データメンバーへのアクセスと静的メンバー関数へのアクセスを含む.
  • 非静的メンバー関数は、静的メンバー関数および静的データメンバーに任意にアクセスすることができる.
  • 静的メンバー関数は、非静的メンバー関数および非静的データメンバーにアクセスできません.
  • thisポインタの追加オーバーヘッドがないため、静的メンバー関数はクラスのグローバル関数に比べて速度的にわずかに増加します.
  • 静的メンバー関数を呼び出し、オペレータ(.)にメンバーでアクセスできます.および(->)1つのクラスのオブジェクトまたはクラスオブジェクトを指すポインタに対して静的メンバー関数を呼び出すか、<クラス名>:<静的メンバー関数名>(<パラメータテーブル>)を直接使用してクラスの静的メンバー関数を呼び出すこともできます.