C 教程
static 是 C/C++ 中很常用的修飾符,它被用來控制變量的存儲方式和可見性。
我們知道在函數(shù)內(nèi)部定義的變量,當程序執(zhí)行到它的定義處時,編譯器為它在棧上分配空間,函數(shù)在棧上分配的空間在此函數(shù)執(zhí)行結(jié)束時會釋放掉,這樣就產(chǎn)生了一個問題: 如果想將函數(shù)中此變量的值保存至下一次調(diào)用時,如何實現(xiàn)? 最容易想到的方法是定義為全局的變量,但定義一個全局變量有許多缺點,最明顯的缺點是破壞了此變量的訪問范圍(使得在此函數(shù)中定義的變量,不僅僅只受此函數(shù)控制)。static 關(guān)鍵字則可以很好的解決這個問題。
另外,在 C++ 中,需要一個數(shù)據(jù)對象為整個類而非某個對象服務(wù),同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內(nèi)部,對外不可見時,可將其定義為靜態(tài)數(shù)據(jù)。
全局(靜態(tài))存儲區(qū):分為 DATA 段和 BSS 段。DATA 段(全局初始化區(qū))存放初始化的全局變量和靜態(tài)變量;BSS 段(全局未初始化區(qū))存放未初始化的全局變量和靜態(tài)變量。程序運行結(jié)束時自動釋放。其中BBS段在程序執(zhí)行之前會被系統(tǒng)自動清0,所以未初始化的全局變量和靜態(tài)變量在程序執(zhí)行之前已經(jīng)為0。存儲在靜態(tài)數(shù)據(jù)區(qū)的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。
在 C++ 中 static 的內(nèi)部實現(xiàn)機制:靜態(tài)數(shù)據(jù)成員要在程序一開始運行時就必須存在。因為函數(shù)在程序運行中被調(diào)用,所以靜態(tài)數(shù)據(jù)成員不能在任何函數(shù)內(nèi)分配空間和初始化。
這樣,它的空間分配有三個可能的地方,一是作為類的外部接口的頭文件,那里有類聲明;二是類定義的內(nèi)部實現(xiàn),那里有類的成員函數(shù)定義;三是應(yīng)用程序的 main() 函數(shù)前的全局數(shù)據(jù)聲明和定義處。
靜態(tài)數(shù)據(jù)成員要實際地分配空間,故不能在類的聲明中定義(只能聲明數(shù)據(jù)成員)。類聲明只聲明一個類的"尺寸和規(guī)格",并不進行實際的內(nèi)存分配,所以在類聲明中寫成定義是錯誤的。它也不能在頭文件中類聲明的外部定義,因為那會造成在多個使用該類的源文件中,對其重復(fù)定義。
static 被引入以告知編譯器,將變量存儲在程序的靜態(tài)存儲區(qū)而非棧上空間,靜態(tài)數(shù)據(jù)成員按定義出現(xiàn)的先后順序依次初始化,注意靜態(tài)成員嵌套時,要保證所嵌套的成員已經(jīng)初始化了。消除時的順序是初始化的反順序。
優(yōu)勢:可以節(jié)省內(nèi)存,因為它是所有對象所公有的,因此,對多個對象來說,靜態(tài)數(shù)據(jù)成員只存儲一處,供所有對象共用。靜態(tài)數(shù)據(jù)成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。
靜態(tài)全局變量有以下特點:
優(yōu)點:靜態(tài)全局變量不能被其它文件所用;其它文件中可以定義相同名字的變量,不會發(fā)生沖突。
(1)全局變量和全局靜態(tài)變量的區(qū)別
一般程序把新產(chǎn)生的動態(tài)數(shù)據(jù)存放在堆區(qū),函數(shù)內(nèi)部的自動變量存放在棧區(qū)。自動變量一般會隨著函數(shù)的退出而釋放空間,靜態(tài)數(shù)據(jù)(即使是函數(shù)內(nèi)部的靜態(tài)局部變量)也存放在全局數(shù)據(jù)區(qū)。全局數(shù)據(jù)區(qū)的數(shù)據(jù)并不會因為函數(shù)的退出而釋放空間。
看下面的例子:
輸出結(jié)果如下:
static 關(guān)鍵字最基本的用法是:
被 static 修飾的變量、被 static 修飾的方法統(tǒng)一屬于類的靜態(tài)資源,是類實例之間共享的,換言之,一處變、處處變。
在 C++ 中,靜態(tài)成員是屬于整個類的而不是某個對象,靜態(tài)成員變量只存儲一份供所有對象共用。所以在所有對象中都可以共享它。使用靜態(tài)成員變量實現(xiàn)多個對象之間的數(shù)據(jù)共享不會破壞隱藏的原則,保證了安全性還可以節(jié)省內(nèi)存。
靜態(tài)成員的定義或聲明要加個關(guān)鍵 static。靜態(tài)成員可以通過雙冒號來使用即 <類名>::<靜態(tài)成員名>。
報錯:
'Point::init' : illegal call of non-static member function
結(jié)論 1:不能通過類名來調(diào)用類的非靜態(tài)成員函數(shù)。
通過類的對象調(diào)用靜態(tài)成員函數(shù)和非靜態(tài)成員函數(shù)。
編譯通過。
結(jié)論 2:類的對象可以使用靜態(tài)成員函數(shù)和非靜態(tài)成員函數(shù)。
在類的靜態(tài)成員函數(shù)中使用類的非靜態(tài)成員。
編譯出錯:
error C2597: illegal reference to data member 'Point::m_x' in a static member function
因為靜態(tài)成員函數(shù)屬于整個類,在類實例化對象之前就已經(jīng)分配空間了,而類的非靜態(tài)成員必須在類實例化對象后才有內(nèi)存空間,所以這個調(diào)用就出錯了,就好比沒有聲明一個變量卻提前使用它一樣。
結(jié)論3:靜態(tài)成員函數(shù)中不能引用非靜態(tài)成員。
在類的非靜態(tài)成員函數(shù)中使用類的靜態(tài)成員。
編譯通過。
結(jié)論 4:類的非靜態(tài)成員函數(shù)可以調(diào)用用靜態(tài)成員函數(shù),但反之不能。
使用類的靜態(tài)成員變量。
按 Ctrl+F7 編譯無錯誤,按 F7 生成 EXE 程序時報鏈接錯誤。
error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)
這是因為類的靜態(tài)成員變量在使用前必須先初始化。
在 main() 函數(shù)前加上 int Point::m_nPointCount = 0; 再編譯鏈接無錯誤,運行程序?qū)⑤敵?1。
結(jié)論 5:類的靜態(tài)成員變量必須先初始化再使用。
思考總結(jié):靜態(tài)資源屬于類,但是是獨立于類存在的。從 J 類的加載機制的角度講,靜態(tài)資源是類初始化的時候加載的,而非靜態(tài)資源是類實例化對象的時候加載的。 類的初始化早于類實例化對象,比如 Class.forName("xxx") 方法,就是初始化了一個類,但是并沒有實例化對象,只是加載這個類的靜態(tài)資源罷 了。所以對于靜態(tài)資源來說,它是不可能知道一個類中有哪些非靜態(tài)資源的;但是對于非靜態(tài)資源來說就不一樣了,由于它是實例化對象出來之后產(chǎn)生的,因此屬于類的這些東西它都能認識。所以上面的幾個問題答案就很明確了:
(static 修飾類:這個用得相對比前面的用法少多了,static 一般情況下來說是不可以修飾類的, 如果 static 要修飾一個類,說明這個類是一個靜態(tài)內(nèi)部類(注意 static 只能修飾一個內(nèi)部類),也就是匿名內(nèi)部類。像線程池 ThreadPoolExecutor 中的四種拒絕機制 CallerRunsPolicy、AbortPolicy、DiscardPolicy、 DiscardOldestPolicy 就是靜態(tài)內(nèi)部類。靜態(tài)內(nèi)部類相關(guān)內(nèi)容會在寫內(nèi)部類的時候?qū)iT講到。)
一般總結(jié):在類中,static 可以用來修飾靜態(tài)數(shù)據(jù)成員和靜態(tài)成員方法。
靜態(tài)數(shù)據(jù)成員
靜態(tài)成員函數(shù)
再給一個利用類的靜態(tài)成員變量和函數(shù)的例子以加深理解,這個例子建立一個學(xué)生類,每個學(xué)生類的對象將組成一個雙向鏈表,用一個靜態(tài)成員變量記錄這個雙向鏈表的表頭,一個靜態(tài)成員函數(shù)輸出這個雙向鏈表。
程序?qū)⑤敵觯?/p>
原文地址:https://www.cnblogs.com/33debug/p/7223869.html