C++的static有兩種用法:面向過程程序設計中的static和麪向對象程序設計中的static。前者應用於普通變量和函數,不涉及類;後者主要說明static在類中的做用。ios
1、面向過程設計中的static數組
一、靜態全局變量函數
在全局變量前,加上關鍵字static,該變量就被定義成爲一個靜態全局變量。咱們先舉一個靜態全局變量的例子,以下:this
//Example 1
#i nclude <iostream.h>
void fn();
static int n; //定義靜態全局變量
void main()
{
n=20;
cout<<n<<endl;
fn();
}
void fn()
{
n++;
cout<<n<<endl;
}
靜態全局變量有如下特色:spa
靜態變量都在全局數據區分配內存,包括後面將要提到的靜態局部變量。對於一個完整的程序,在內存中的分佈狀況以下圖:
設計
代碼區 |
全局數據區 |
堆區 |
棧區 |
通常程序的由new產生的動態數據存放在堆區,函數內部的自動變量存放在棧區。自動變量通常會隨着函數的退出而釋放空間,靜態數據(即便是函數 內部的靜態局部變量)也存放在全局數據區。全局數據區的數據並不會由於函數的退出而釋放空間。細心的讀者可能會發現,Example 1中的代碼中將指針
static int n; //定義靜態全局變量
改成rest
int n; //定義全局變量
程序照樣正常運行。
的確,定義全局變量就能夠實現變量在文件中的共享,但定義靜態全局變量還有如下好處:orm
您能夠將上述示例代碼改成以下:對象
//Example 2
//File1
#i nclude <iostream.h>
void fn();
static int n; //定義靜態全局變量
void main()
{
n=20;
cout<<n<<endl;
fn();
}
//File2
#i nclude <iostream.h>
extern int n;
void fn()
{
n++;
cout<<n<<endl;
}
編譯並運行Example 2,您就會發現上述代碼能夠分別經過編譯,但運行時出現錯誤。試着將
static int n; //定義靜態全局變量
改成
int n; //定義全局變量
再次編譯運行程序,細心體會全局變量和靜態全局變量的區別。
二、靜態局部變量
在局部變量前,加上關鍵字static,該變量就被定義成爲一個靜態局部變量。
咱們先舉一個靜態局部變量的例子,以下:
//Example 3
#i nclude <iostream.h>
void fn();
void main()
{
fn();
fn();
fn();
}
void fn()
{
static n=10;
cout<<n<<endl;
n++;
}
一般,在函數體內定義了一個變量,每當程序運行到該語句時都會給該局部變量分配棧內存。但隨着程序退出函數體,系統就會收回棧內存,局部變量也相應失效。
但有時候咱們須要在兩次調用之間對變量的值進行保存。一般的想法是定義一個全局變量來實現。但這樣一來,變量已經再也不屬於函數自己了,再也不僅受函數的控制,給程序的維護帶來不便。
靜態局部變量有如下特色:
三、靜態函數
在函數的返回類型前加上static關鍵字,函數即被定義爲靜態函數。靜態函數與普通函數不一樣,它只能在聲明它的文件當中可見,不能被其它文件使用。靜態函數只能操做靜態成員變量(有待商榷。。。),還能夠做爲回調函數,最重要的是函數沒有this指針。
靜態函數的例子:
//Example 4
#include <iostream.h>
static void fn();//聲明靜態函數
void main()
{
fn();
}
void fn()//定義靜態函數
{
int n=10;
cout<<n<<endl;
}
定義靜態函數的好處:
2、面向對象的static關鍵字(類中的static關鍵字)
一、靜態數據成員
在類內數據成員的聲明前加上關鍵字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();
}
能夠看出,靜態數據成員有如下特色:
二、靜態成員函數
與靜態數據成員同樣,咱們也能夠建立一個靜態成員函數,它爲類的所有服務而不是爲某一個類的具體對象服務。靜態成員函數與靜態數據成員同樣,都是類的內部實現,屬於類定義的一部分。普通的成員函數通常都隱含了一個this指針,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() //靜態成員函數的實現,注意此處無static
{
// 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();
}
關於靜態成員函數,能夠總結爲如下幾點:
(1)爲何須要類靜態成員?
有時候某個類的多個對象須要訪問一個全局對象,在這種狀況下「提供一個全局對象」比「每一個類都維持一個獨立的數據成員」要更爲有效。而類靜態成員與全局變量相比,又有兩點好處:(a)不存在與程序中其餘全局名字衝突的可能性;(b)可設置爲private,實現信息隱藏。
(2)類靜態成員的特色
對於非靜態數據成員,每一個類對象都有本身的拷貝,而靜態數據成員對每一個類類型只有一個拷貝。因爲靜態數據成員分配在全局數據區,所以在程序開始運行時就必須存在,故靜態數據成員的空間分配和初始化不可能在main函數或其餘函數中完成(於是在局部類中也不容許出現靜態數據成員)。這樣一來,靜態數據成員的空間分配和初始化只可能有如下三種途徑:(a)類的頭文件:這裏有類的聲明。但存在一個沒法迴避的問題:對於靜態數據成員,在程序中也只能存在一個定義,而類的頭文件可能被重複引用而出現重複定義;(b)main函數前的全局數據聲明和定義處:這種方法也有問題,每個使用該類的程序都必須在此處定義一下該類的靜態成員,這是不現實的;(c)類定義的內部實現:這是最爲理想的方式,引用時只需包含頭文件便可。
#include "account.h"
double Account::_interestRate = 0.05
(3)類靜態成員的定義
在對靜態數據成員進行定義時有如下幾點注意事項:(a)在類定義以外定義時,靜態成員的名字必須被其類名限定修飾,前不可再添加static;(b)像int等有序類型的靜態數據成員可在類定義中初始化,但仍需在類定義外進行定義,但此時已不能指定初始值;
// 頭文件
class Account
{
// ...
private:
static const int nameSize = 16;//內部初始化
static const char name[nameSize];//注意可用nameSize定義數組(此變量必須同時爲靜態和常量,且在類內部初始化)
};
// 文本文件
const int Account::nameSize; // 若已經在類內部初始化,此句無關緊要
const char Account::name[nameSize] = "Saving Account";//外部定義並初始化
(4) 類靜態成員的訪問:
在類的成員函數中能夠直接訪問該類的靜態數據成員,而在非成員函數中可以使用成員訪問操做符或類名限定修飾的方式進行訪問。
(5)靜態成員函數:
靜態成員函數沒法訪問屬於具體類對象的非靜態數據成員,也沒法訪問非靜態成員函數,他只能訪問靜態數據成員和調用其他的靜態成員函數。這樣一來,就能防止類的非靜態數據成員遭受竄改。靜態成員函數沒有this指針,任何在靜態成員函數中顯式或隱式地引用這個指針都將致使編譯時刻錯誤。