static 有兩種用法:面向過程程式設計中的static 和面向對象程式設計中的static。前者應用于普通變量和
函數,不涉及類;後者主要說明 static 在類中的作用。
一、面向過程設計中的static
全局變量、局部變量、靜态全局變量、靜态局部變量的差別
C++變量根據定義的位置的不同的生命周期,具有不同的作用域,作用域可分為 6種:全局作用域,局部
作用域,語句作用域,類作用域,命名空間作用域和檔案作用域。
從作用域看:
全局變量具有全局作用域。全局變量隻需在一個源檔案中定義,就可以作用于所有的源檔案。當然,其他
不包含全局變量定義的源檔案需要用 extern 關鍵字再次聲明這個全局變量。
靜态局部變量具有局部作用域,它隻被初始化一次,自從第一次被初始化直到程式運作結束都一直存在,
它和全局變量的差別在于全局變量對所有的函數都是可見的,而靜态局部變量隻對定義自己的函數體始終
可見。
局部變量也隻有局部作用域,它是自動對象(auto),它在程式運作期間不是一直存在,而是隻在函數執
行期間存在,函數的一次調用執行結束後,變量被撤銷,其所占用的記憶體也被收回。
靜态全局變量也具有全局作用域,它與全局變量的差別在于如果程式包含多個檔案的話,它作用于定義它
的檔案裡,不能作用到其它檔案裡,即被 static 關鍵字修飾過的變量具有檔案作用域。這樣即使兩個不同
的源檔案都定義了相同名字的靜态全局變量,它們也是不同的變量。
從配置設定記憶體空間看:
全局變量,靜态局部變量,靜态全局變量都在靜态存儲區配置設定空間,而局部變量在棧裡配置設定空間
全局變量本身就是靜态存儲方式, 靜态全局變量當然也是靜态存儲方式。這兩者在存儲方式上并無不同。
這兩者的差別雖在于非靜态全局變量的作用域是整個源程式,當一個源程式由多個源檔案組成 時,非靜态
的全局變量在各個源檔案中都是有效的。 而靜态全局變量則限制了其作用域, 即隻在定義該變量的源文
件内有效,在同一源程式的其它源檔案中不能使用它。由于靜态全局變量的作用域局限于一個源檔案内,
隻能為該源檔案内的函數公用,因 此可以避免在其它源檔案中引起錯誤。
1)、靜态變量會被放在程式的靜态資料存儲區(資料段)(全局可見)中,這樣可以在下一次調用的時候還可
以保持原來的指派。這一點是它與堆棧變量和堆變量的差別。
2)、變量用static 告知編譯器,自己僅僅在變量的作用範圍内可見。這一點是它與全局變量的差別。
從以上分析可以看出, 把局部變量改變為靜态變量後是改變了它的存儲方式即改變了它的生存期。把全局
變量改變為靜态變量後是改變了它的作用域,限制了它的使用範圍。是以 static 這個說明符在不同的地方
所起的作用是不同的。應予以注意。
Tips:
A.若全局變量僅在單個C檔案中通路,則可以将這個變量修改為靜态全局變量,以降低子產品間的耦合度;
B.若全局變量僅由單個函數通路,則可以将這個變量改為該函數的靜态局部變量,以降低子產品間的耦合度;
C.設計和使用通路動态全局變量、靜态全局變量、靜态局部變量的函數時,需要考慮重入問題,因為他們
都放在靜态資料存儲區,全局可見;
D.如果我們需要一個可重入的函數,那麼,我們一定要避免函數中使用 static 變量(這樣的函數被稱為:
帶“内部存儲器”功能的的函數)
E.函數中必須要使用static 變量情況:比如當某函數的傳回值為指針類型時,則必須是static 的局部變量的位址作為傳回值,若為auto類型,則傳回為錯指針。
-----------------------------------------------------------------------------------------------------------
static 全局變量:改變作用範圍,不改變存儲位置
static 局部變量:改變存儲位置,不改變作用範圍
靜态函數 :在函數的傳回類型前加上static 關鍵字,函數即被定義為靜态函數。靜态函數與普通函數不同,
它隻能在聲明它的檔案當中可見,不能被其它檔案使用。
如果在一個源檔案中定義的函數,隻能被本檔案中的函數調用,而不能被同一程式其它檔案中
的函數調用,這種函數也稱為内部函數。定義一個内部函數,隻需在函數類型前再加一個“static”關鍵字即
可。
---------------------------------------------------------------------------------------------------------------
二、面向對象的static 關鍵字(類中的static 關鍵字)
靜态資料成員有以下特點:
對于非靜态資料成員,每個類對象都有自己的拷貝。而靜态資料成員被當作是類的成員。無論這個類的對
象被定義了多少個,靜态資料成員在程式中也隻有一份 拷 貝,由該類型的所有對象共享通路。也就是說,
靜态資料成員是該類的所有對象所共有的。對該類的多個對象來說,靜态資料成員隻配置設定一次記憶體,供所
有對象共 用。是以,靜态資料成員的值對每個對象都是一樣的,它的值可以更新;
靜态資料成員存儲在全局資料區。靜态資料成員定義時要配置設定空間,是以不能在類聲明中定義。在 Example
5中,語句int Myclass::Sum=0;是定義靜态資料成員;
靜态資料成員和普通資料成員一樣遵從 public,protected,private 通路規則;
因為靜态資料成員在全局資料區配置設定記憶體,屬于本類的所有對象共享,是以,它不屬于特定的類對象,在
沒有産生類對象時其作用域就可見,即在沒有産生類的執行個體時,我們就可以操作它;
靜态資料成員初始化與一般資料成員初始化不同。靜态資料成員初始化的格式為:
<資料類型><類名>::<靜态資料成員名>=<值>
類的靜态資料成員有兩種通路形式:
<類對象名>.<靜态資料成員名> 或 <類類型名>::<靜态資料成員名>
如果靜态資料成員的通路權限允許的話(即 public 的成員),可在程式中,按上述格式來引用靜态資料成
員 ;
靜态資料成員主要用在各個對象都有相同的某項屬性的時候。比如對于一個存款類,每個執行個體的利息都是
相同的。是以,應該把利息設為存款類的靜态資料成 員。這 有兩個好處,第一,不管定義多少個存款類
對象,利息資料成員都共享配置設定在全局資料區的記憶體,是以節省存儲空間。第二,一旦利息需要改變時,
隻要改變一次, 則所有存款類對象的利息全改變過來了;
同全局變量相比,使用靜态資料成員有兩個優勢:
靜态資料成員沒有進入程式的全局名字空間,是以不存在與程式中其它全局名字沖突的可能性;
可以實作資訊隐藏。靜态資料成員可以是 private成員,而全局變量不能;
2、靜态成員函數
與靜态資料成員一樣,我們也可以建立一個靜态成員函數,它為類的全部服務而不是為某一個類的具體對
象服務。靜态成員函數與靜态資料成員一樣,都是類的 内部 實作,屬于類定義的一部分。 普通的成員函
數一般都隐含了一個this 指針,this 指針指向類的對象本身,因為普通成員函數總是具體的屬于某個類的
具體對象的。通常情況下,this 是預設的。如函數fn()實際上是this->fn()。但是與普通函數相比,靜态成
員函數由于不是與任何的對象相聯系,是以它不具有this 指 針。從這個意義上講,它無法通路屬于類對象
的非靜态資料成員,也無法通路非靜态成員函數,它隻能調用其餘的靜态成員函數。
關于靜态成員函數,可以總結為以下幾點:
出現在類體外的函數定義不能指定關鍵字 static;
靜态成員之間可以互相通路,包括靜态成員函數通路靜态資料成員和通路靜态成員函數; 非靜态成員函數可以任意地通路靜态成員函數和靜态資料成員;
靜态成員函數不能通路非靜态成員函數和非靜态資料成員;
由于沒有this 指針的額外開銷,是以靜态成員函數與類的全局函數相比速度上會有少許的增長;
調用靜态成員函數,可以用成員通路操作符(.)和(->)為一個類的對象或指向類對象的指針調用靜态成員函
數,也可以直接使用如下格式:
<類名>::<靜态成員函數名>(<參數表>)
調用類的靜态成員函數。
===============================================================================
================
static 靜态變量聲明符。 在聲明它的程式塊,子程式塊或函數内部有效,值保持,在整個程式期間配置設定存
儲器空間,編譯器預設值0。
是C++中很常用的修飾符,它被用來控制變量的存儲方式和可見性。
2、為什麼要引入static?
函數内部定義的變量,在程式執行到它的定義處時,編譯器為它在棧上配置設定空間,大家知道,函數在棧上
配置設定的空間在此函數執行結束時會釋放掉,這樣就産生 了一個問題: 如果想将函數中此變量的值儲存至下
一次調用時,如何實作? 最容易想到的方法是定義一個全局的變量,但定義為一個全局變量有許多缺點,
最明顯的缺點是破壞了此變量的通路範圍(使得在此函數中定義的變量,不僅僅受此 函數控制)。
3、什麼時候用static?
需要一個資料對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隐藏在類的内
部,對外不可見。
4、static 的内部機制:
靜态資料成員要在程式一開始運作時就必須存在。因為函數在程式運作中被調用,是以靜态資料成員不能
在任何函數内配置設定空間和初始化。
這樣,它的空間配置設定有三個可能的地方,一是作為類的外部接口的頭檔案,那裡有類聲明;二是類定義的
内部實作,那裡有類的成員函數定義;三是應用程式的 main()函數前的全局資料聲明和定義處。
靜态資料成員要實際地配置設定空間,故不能在類的聲明中定義(隻能聲明資料成員)。類聲明隻聲明一個類
的“尺寸和規格”,并不進行實際的記憶體配置設定,是以在 類聲明中寫成定義是錯誤的。它也不能在頭檔案中類
聲明的外部定義,因為那會造成在多個使用該類的源檔案中,對其重複定義。
static 被引入以告知編譯器,将變量存儲在程式的靜态存儲區而非棧上空間,靜态
資料成員按定義出現的先後順序依次初始化,注意靜态成員嵌套時,要保證所嵌套的成員已經初始化了。
消除時的順序是初始化的反順序。
5、static 的優勢:
可以節省記憶體,因為它是所有對象所公有的,是以,對多個對象來說,靜态資料成員隻存儲一處,供所有
對象共用。靜态資料成員的值對每個對象都是一樣,但它的值是可以更新的。隻要對靜态資料成員的值更
新一次,保證所有對象存取更新後的相同的值,這樣可以提高時間效率。
6、引用靜态資料成員時,采用如下格式:
<類名>::<靜态成員名>
如果靜态資料成員的通路權限允許的話(即public 的成員),可在程式中,按上述格式
來引用靜态資料成員。
7、注意事項:
(1)類的靜态成員函數是屬于整個類而非類的對象,是以它沒有 this 指針,這就導緻
了它僅能通路類的靜态資料和靜态成員函數。
(2)不能将靜态成員函數定義為虛函數。
(3)由于靜态成員聲明于類中,操作于其外,是以對其取位址操作,就多少有些特殊 ,變量位址是指向其資料類型的指針 ,函數位址類型是一個“nonmember函數指針”。
(4)由于靜态成員函數沒有this 指針,是以就差不多等同于nonmember函數,結果就
産生了一個意想不到的好處:成為一個 callback 函數,使得我們得以将C++和C-based X W
indow 系統結合,同時也成功的應用于線程函數身上。
(5)static 并沒有增加程式的時空開銷,相反她還縮短了子類對父類靜态成員的通路
時間,節省了子類的記憶體空間。
(6)靜态資料成員在<定義或說明>時前面加關鍵字static。
(7)靜态資料成員是靜态存儲的,是以必須對它進行初始化。
(8)靜态成員初始化與一般資料成員初始化不同:
初始化在類體外進行,而前面不加 static,以免與一般靜态變量或對象相混淆;
初始化時不加該成員的通路權限控制符 private,public 等;
初始化時使用作用域運算符來标明它所屬類;
是以我們得出靜态資料成員初始化的格式:
<資料類型><類名>::<靜态資料成員名>=<值>
(9)為了防止父類的影響,可以在子類定義一個與父類相同的靜态變量,以屏蔽父類的影響。這裡有一點需
要注意:我們說靜态成員為父類和子類共享,但我 們有重複定義了靜态成員,這會不會引起錯誤呢?不會,
我們的編譯器采用了一種絕妙的手法:name-mangling 用以生成唯一的标志。