解析static!

[size=18:7291e6e16b][color=darkblue:7291e6e16b][b:7291e6e16b]解析static![/b:7291e6e16b][/color:7291e6e16b][/size:7291e6e16b]

http://ASP.6to23.com/vcprogram/articlemanage/article/jxstatic.asp?title=解析static&index=58

通常理解static只是指靜態存儲的概念,事實上在c++裏面static包含了兩方面的含義。

1)在固定地址上的分配,這意味著對象是在一個非凡的靜態區域上創建的,而不是每次函數調用的時候在堆棧上動態創建的,這是static的靜態存儲的概念。

2) 另一方面,static能夠控制對象對于連接器的可見性。一個static對象,對于特定的編譯單元來說總是本地範圍的,這個範圍包括本地文件或者本地的某一個類,超過這個範圍的文件或者類是不可以看到static對象的,這同時也描述了連接的概念,它決定連接器能夠看到哪些名字。

[b:7291e6e16b]一,關于靜態存儲[/b:7291e6e16b]

對于一個完整的程序,他們在內存中的分布情況如下圖:

[img:7291e6e16b]http://asp.6to23.com/vcprogram/articlemanage/image/static.jpg[/img:7291e6e16b]

一般程序的由malloc,realloc産生的動態數據存放在堆區,程序的局部數據即各個函數內部的數據存放在棧區,局部數據對象一般會隨著函數的退出而釋放空間,對于static數據即使是函數內部的對象則存放在全局數據區,全局數據區的數據並不會因爲函數的退出就將空間釋放。

[b:38f65016cf]二,函數內部的靜態變量[/b:38f65016cf]

通常,在函數體內定義一個變量的時候,編譯器使得每次函數調用時候堆棧的指針向下移動一個合適的位置,爲這些內部變量分配內存。假如這個變量是一個初始化表達式,那麽每當程序運行到這兒的時候程序都需要對表達式進行初始化。這種情況下變量的值在兩次調用之間則不能進行保存。

有的時候我們卻需要在兩次調用之間對變量的值進行保存,通常的想法是定義一個全局變量來實現。但這樣一來,變量已經不再屬于函數本身了,不再僅受函數的控制。因此我們有必要將變量聲明爲static對象,這樣對象將保存在靜態存儲區域,而不是保存在堆棧中。對象的初始化僅僅在函數第一個被調用的時候進行初始化,每次的值保持到下一次調用,直到新的值覆蓋它。下面的例子解釋了這一點。

[code:1:38f65016cf]//****************************************

// 1.cpp

//****************************************

1 #include <iostream.h>

2 void add_n(void);

3 void main(){

4 int n=0;

5 add_n();

6 add_n();

7 add_n();

8 }

9 void add_n(void){

10 staticn=50;

11 cout<<”n=”<<n<<endl;

12 n++;

13 }[/code:1:38f65016cf]

程序運行結果爲

[code:1:38f65016cf]n=50

n=51

n=52;[/code:1:38f65016cf]

HopeCao 回複于:2003-06-18 14:32:41

從上面的運行結果可以看出static n確實是在每次調用中都保持了上一次的值。假如預定義的靜態變量沒有提供一個初始值的話,編譯器會確保在初始化的時候將用零值爲其初始化。static變量必須被初始化,但是零值初始化僅僅只對系統預定義類型有效,比如int,char,bool等等。事實上我們用到的不僅僅是這些預定義類型,大多數情況下可能用到結構,聯合或者類等各種用戶自定義的類型,對于這些類型用戶必須使用構造函數進行初始化。假如我們在定義一個靜態對象的時候沒有指定構造函數的參數,那就必須使用缺省的構造函數,假如缺省的構造函數也沒有的話則會出錯。看下面的例子。

[code:1:30a24d7c00]//**************************************************

// 2.cpp

//**************************************************

1 #include <isotream.h>

2 class x{

3 int i;

4 public:

5 x(int i=0):i(i){

6 cout<<”i=”<<i<<endl;

7 }//缺省構造函數

8 ~x(){cout<<”x::~x()”<<endl;

9 };

10 void fn(){

11 staticx x1(47);

12 staticx x2;

13 }

14 main(){

15 fn();

16 }[/code:1:30a24d7c00]

程序運行結果如下

[code:1:30a24d7c00]i=47

i=0

x::~x()

x::~x()

[/code:1:30a24d7c00]

HopeCao 回複于:2003-06-18 14:34:19

從上面的例子可以看出靜態的x對象既可以使用帶參數的構造函數進行初始化,象x1,也可以使用缺省的構造函數,象x2。程序控制第一次轉到對象的定義點時候,而且只有第一次的時候才需要執行構造函數。假如對象既沒有帶參數的構造函數又沒有缺省構造函數則程序將無法通過編譯。

[b:eaa8a56606]三,類中的靜態成員[/b:eaa8a56606]

static靜態存儲的概念可以進一步引用到類中來。c++中的每一個類的對象都是該類的成員的拷貝,一般情況下它們彼此之間沒有什麽聯系,但有時候我們需要讓它們之間共享一些數據,我們可以通過全局變量來實現,但是這樣的結果是導致程序的不安全性,因爲任何函數都可以對這個變量進行訪問和修改,而且輕易與項目中的其余的名字産生沖突,因此我們需要一種兩全其美的方法,既可以當成全局數據變量那樣存儲,又可以隱藏在類的內部,與類本身聯系起來,這樣只有類的對象才可以操縱這個變量,從而增加了變量的安全性。

這種變量我們稱之爲類的靜態成員,靜態成員包括靜態數據成員和靜態成員函數。類的所有的靜態數據成員有著單一的存儲空間而不管類的對象由多少,這些對象共享這塊存儲區域。因此每一個類的對象的靜態成員發生改變都會對其余的對象産生影響。先看下面的例子。

[code:1:eaa8a56606]//**************************************

// student.cpp

//**************************************

1. #include <iostream.h>

2. #include <string.h>

3. class student{

4. public:

5. student(char* pname=”no name”){

6. cout<<”create one student”<endl;

7. strcpy(name,pname);

8. number++;

9. cout<<number<<endl;

10. }

11. ~student() {

12. cout<<”destrUCt one student”<<endl;

13. number--;

14. cout<<number<<endl;

15. }

16. static number(){

17. return number; }

18. protected:

19. char nme[40]

20. staticint number;

21. }

22. void fn(){

23. student s1;

24. student s2;

25. cout<<student::number<<endl;

26. }

27. main(){

28. fn();

29. cout<<student::number<<endl;

30. }[/code:1:eaa8a56606]

程序輸出結果如下:

[code:1:eaa8a56606]create one student

1

create one student

2

2

destruct one student

1

destruct one student

0

0[/code:1:eaa8a56606]

上面的程序代碼中我們使用了靜態數據成員和靜態函數成員,下面我們先闡述靜態數據成員。

HopeCao 回複于:2003-06-18 14:38:32

[b:5282547077]四,靜態數據成員[/b:5282547077]

在代碼中我們可以看出,number既不是對象s1也不是對象s2的一部分,它是屬于student這個類的。每一個 student對象都有name成員,但是number成員卻只有一個,所有的student對象共享使用這個成員。s1.number與 s2.number是等值的。在student的對象空間中沒有爲number成員保留空間,它的空間分配不在student的構造函數裏完成,空間回收也不再析構函數裏面完成。因此與name成員不一樣,它不會隨著對象的産生或者消失而産生或消失。

由于靜態數據成員的空間分配在全局數據區,因此在程序一開始運行的時候就必須存在,所以靜態成員的空間的分配和初始化不可能在函數包括main主函數中完成,因爲函數在程序中被調用的時候才爲內部的對象分配空間。這樣,靜態成員的空間分配和初始化只能由下面的三種途徑。一是類的外部接口的頭文件,那裏聲明了類定義。二是類定義的內部實現,那裏有類成員函數的定義和具體實現。三是應用程序的main()函數前的全局數據聲明和定義處。由于靜態數據成員必須實際的分配空間,因此不可能在類定義頭文件中分配內存。另一方面也不能在頭文件中類聲明的外部定義,因爲那會造成多個使用該類的源程序重複出現定義。靜態數據成員也不能在main()函數的全局數據聲明處定義。假如那樣的話每一個使用該類的程序都必須在程序的 main()函數的全局數據聲明處定義一下該類的靜態成員。這是不現實的。唯一的辦法就是將靜態數據成員的定義放在類的實現中。定義時候用類名引導,引用時包含頭文件即可。

例如[code:1:5282547077]class a{

staticint i;

public:

//

}

在類定義文件中 int a::i=1;[/code:1:5282547077]

[color=blue:5282547077]有兩點需要注重的是[/color:5282547077]

[color=blue:5282547077]1.靜態數據成員必須且只能定義一次,假如重複定義,連接器會報告錯誤。同時它們的初始化必須在定義的時候完成。對于預定義類型的靜態數據成員,假如沒有賦值則賦零值。對于用戶自定義類型必須通過構造函數賦值,假如沒有構造函數包括缺省構造函數,編譯無法通過。

2. 局部類中不答應出現靜態數據成員。因爲靜態數據成員必須在程序運行時候就存在這導致程序無法爲局部類中的靜態數據成員分配空間。下面的代碼是不答應的。[/color:5282547077]

[code:1:5282547077]void fn(){

class foo{

staticint i;//定義非法。

public:

// }

}[/code:1:5282547077]

HopeCao 回複于:2003-06-18 14:43:07

[b:4008a128d4]五,靜態函數成員[/b:4008a128d4]

與靜態數據成員一樣,我們也可以創建一個靜態成員函數,它爲類的全部服務而不是爲某一個類的具體對象服務。靜態函數成員與靜態成員一樣,都是類的內部實現,屬于類定義的一部分。程序student.cpp中的number()就是靜態成員函數,它的定義位置與一般成員函數相同。

普通的成員函數一般都隱含了一個this指針,this指針指向類的對象本身,因爲普通成員函數總是具體的屬于某個類的具體對象的。通常情況下 this是缺省的。如函數add()實際上是寫成this.add().但是與普通函數相比,靜態成員函數由于不是與任何的對象相聯系因此它不具有 this指針,從這個意義上講,它無法訪問屬于具體類對象的非靜態數據成員,也無法訪問非靜態成員函數,它只能調用其余的靜態成員函數。如下面的代碼

[code:1:4008a128d4]1 class x {

2 int i;

3 staticint j;

4 public:

5 x(int i=0):i(i){

6 j=i

7 }//非靜態成員函數可以訪問靜態函數成員和靜態數據成員。

8 int val() const {return i;}

9 staticint incr(){

10 i++;//錯誤的代碼,因爲i是非靜態成員,因此incr()作爲靜態成員函數無

11 //法訪問它。

12 return ++j;

13 }

14 staticint fn(){

15 return incr();//合法的訪問,因爲fn()和incr()作爲靜態成員函數可以互相訪

16 //問。

17 }

18 };

19 int x::j=0;

20 main(){……}[/code:1:4008a128d4]

li2002 回複于:2003-06-18 14:44:15

好啊,以前不了解static,現在知道了,建議加入精華!

HopeCao 回複于:2003-06-18 14:49:34

[color=blue:17500aa702]根據上面的程序可以總結幾點:

1. 靜態成員之間可以相互的訪問包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數。

2. 非靜態成員函數可以任意的訪問靜態成員函數和靜態數據成員。

3. 靜態成員函數不能訪問非靜態成員函數和非靜態數據成員。[/color:17500aa702]

由于沒有this指針的額外開銷,因此靜態成員函數與全局函數相比速度上會有少許的增長。

[b:17500aa702]六,理解控制連接[/b:17500aa702]

理解控制連接之前我們先了解一下外部存儲類型的概念。一般的在程序的規模很小的情況下我們用一個源程序既可以表達完整。但事實上稍微有價值的程序都不可能只用一個程序來表達的,而是分割成很多的小的模塊,每一個模塊完成特定的功能,構成一個源文件。所有的源文件共享一個main函數。在不同的源文件之間爲了能夠相互進行數據或者函數的溝通,這時候通常聲明數據或者函數爲extern,這樣用extern聲明的數據或者函數就是全局變量或者函數。默認情況下的函數的聲明總是extern的。在文件範圍內定義的全局數據和函數對程序中的所有的編譯單元來說都是可見的。這就是通常我們所說的外部連接。

但有時候我們可能想限制一個名字對象的可見性,想讓一個對象在本地文件範圍內是可見的,這樣這個文件中的所有的函數都可以利用這個對象,但是不想讓本地文件之外的其余文件看到或者訪問該對象,或者外部已經定義了一個全局對象,防止內部的名字對象與之沖突。

通過在全局變量的前面加上static,我們可以做到這一點。在文件的範圍內,一個被聲明爲static的對象或者函數的名字對于編譯單元來說是局部變量,我們稱之爲靜態全部變量。這些名字不使用默認情況下的外部連接,而是使用內部連接。從下面的例子可以看出static是如何控制連接的。

工程文件爲first.prj由兩個源文件組成。

[code:1:17500aa702]exam1.cpp

exam2.cpp

//***************************************

// exam1.cpp

//***************************************

1 #include <iostream.h>

2 int n;

3 void print_n();

4 void main()

5 {

6 n=20;

7 cout<<n<<endl;

8 print_n();

9 }

//****************************************

// exam2.cpp

//****************************************

10 staticn;

11 staticvoid staticfn();

12 void print_n()

13 {

14 n++;

15 cout<<n<<endl;

16 staticfn();

17 }

18 void staticfn()

19 {

20 cout<<n++<<endl;

21 }[/code:1:17500aa702]

程序運行結果如下

[code:1:17500aa702]20

1

1[/code:1:17500aa702]

下面我們將對上面的程序進行少量的改造,在看看執行結果如何。

[color=darkred:17500aa702]改造一:[/color:17500aa702]

[code:1:17500aa702]將exam1.cpp的第二行int n改爲extern int n,這就是告訴程序我在此處聲明了變量n,但是真正的定義過程在別的文件中,此處就是exam2.cpp。但事實上exam2.cpp中僅僅聲明了 static int n。我們看看運行結果。vc中會通過編譯,但是在進行連接時候會給出一個“變量n找不到”的錯誤。這說明 exam1.cpp無法共享exam2.cpp中的static int n變量。[/code:1:17500aa702]

[color=darkred:17500aa702]改造二:[/color:17500aa702]

[code:1:17500aa702]我們在exam1.cpp的第二行和第三行之間增加void staticfn();同時在第八行和第九行之間增加staticfn()的調用。再看執行結果。vc會産生一個找不到函數staticfn的錯誤。這說明exam1.cpp無法共享 exam2.cpp中的staticfn()。[/code:1:17500aa702]

[color=blue:17500aa702]從上面的結論可以看出下面幾點:[/color:17500aa702]

[code:1:17500aa702]1. static解決了名字的沖突問題。使得可以在源文件中建立並使用與其它源文件甚至全局變量一樣的名字而不會導致沖突的産生。這一點在很大的項目中是很有用處的。

2. 聲明爲靜態的函數不能被其他的源文件所調用,因爲它的名字只對本地文件可見,其余的文件無法獲取它的名字,因此不可能進行連接。

在文件作用域下聲明的inline函數默認情況下認爲是static類型。在文件作用域下聲明的const的常量默認情況下也是static存儲類型的。[/code:1:17500aa702]

解析static!
  [size=18:7291e6e16b][color=darkblue:7291e6e16b][b:7291e6e16b]解析static![/b:7291e6e16b][/color:7291e6e16b][/size:7291e6e16b]  http://asp.6to23.com/vcprogram/articlemanage/article/jxstatic.asp?t...查看完整版>>解析static!
 
PHP延遲靜態捆綁Late Static Bindings
看了一下PHP5.3的Late Static Bindings,簡單了翻譯一下Late Static Bindings是在PHP5.3中加入的新特性,拼音來說,就是把本來在定義階段固定下來的表達式或變量,改在執行階段才決定,比如當一個子類繼承了父類的靜態表達...查看完整版>>PHP延遲靜態捆綁Late Static Bindings
 
Macro.Word97.Static
病毒名稱(中文): 病毒別名: 威脅級別: ★★☆☆☆ 病毒類型: 宏病毒 病毒長度: 影響系統:DOS病毒行爲: Word的宏病毒,主要通過Word文檔進行感染。...查看完整版>>Macro.Word97.Static
 
C++中的 static 關鍵字
文本要害字:程序設計/C++/技巧前言:  本文只是本人對C++中關于靜態類型的一個總結,如錯誤之處,請大家幫我改正。我分兩個方面來總結,第一方面主要是相對于面向過程而言,即在這方面不涉及到類,第二方面相對于...查看完整版>>C++中的 static 關鍵字
 
c語言中static變量
  1. static 變量靜態變量的類型 說明符是static。 靜態變量當然是屬于靜態存儲方式,但是屬于靜態存儲方式的量不一定就是靜態變量。 例如外部變量雖屬于靜態 存儲方式,但不一定是靜態變量,必須由 static加以定...查看完整版>>c語言中static變量
 
Is it a problem on Regex or Static
I have met a problem, which will seriously cause JVM problem.I try to build a class to solve String operation with Regular EXPression (Java.util.regex.*)I define all the method as a static one, thus i...查看完整版>>Is it a problem on Regex or Static
 
Java中Static、this、super、final用法
  一、Static    請先看下面這段程序:    TER public class Hello  {  public static void main(String[] args)  {  //(1)  System.out.println("Hello,world!");  //(2)  }  }    看...查看完整版>>Java中Static、this、super、final用法
 
Thinking:Java中static、this、super、final用法
Thinking:Java中static、this、super、final用法 本篇旨在幫助預備學習Java以及剛接觸Java的朋友熟悉、把握和使用static、this、super、final這幾個要害字的使用。Java博大精深,我也是一位正在學習和使用Java的愛好...查看完整版>>Thinking:Java中static、this、super、final用法
 
Java修飾符之static
  Static這個修飾符相信大家不生疏吧,沒錯它就是靜態修飾符,什麽叫靜態修飾符呢?大家都知道,在程序中任何變量或者代碼都是在編譯時由系統自動分配內存來存儲的,而所謂靜態就是指在編譯後所分配的內存會一直存...查看完整版>>Java修飾符之static
 
 
回到王朝網路移動版首頁