C/C++ static
static 是什么?
static 是C++中的一個修飾符,它用來控制變量的存儲方式和可見性。
為什么要用static
因為函數內部定義的變量,當程序執行到它的定義處時,編譯器為它在棧上分配空間,函數在棧上分配的空間在此函數執行結束時會釋放掉。如果想將函數中此變量的值保存至下一次調用時,如何實現?使用全局的變量(使得在此函數中定義的變量,不僅僅只受此函數控制)。static 關鍵字則可以很好的解決這個問題。
什么時候用
當需要一個數據對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見時,可將其定義為靜態數據。
靜態數據的存儲
全局(靜態)存儲區
全局(靜態)存儲區:分為 DATA 段和 BSS 段。DATA 段(全局初始化區)存放初始化的全局變量和靜態變量;BSS 段(全局未初始化區)存放未初始化的全局變量和靜態變量。程序運行結束時自動釋放。其中BBS段在程序執行之前會被系統自動清0,所以未初始化的全局變量和靜態變量在程序執行之前已經為0。存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是唯一的一次初始化。
在 C++ 中 static 的內部實現機制:
靜態數據成員要在程序一開始運行時就必須存在。因為函數在程序運行中被調用,所以靜態數據成員不能在任何函數內分配空間和初始化。
這樣,它的空間分配有三個可能的地方,一是作為類的外部接口的頭文件,那里有類聲明;二是類定義的內部實現,那里有類的成員函數定義;三是應用程序的 main() 函數前的全局數據聲明和定義處。
靜態數據成員要實際地分配空間,故不能在類的聲明中定義(只能聲明數據成員)。類聲明只聲明一個類的"尺寸和規格",并不進行實際的內存分配,所以在類聲明中寫成定義是錯誤的。它也不能在頭文件中類聲明的外部定義,因為那會造成在多個使用該類的源文件中,對其重復定義。
static 被引入以告知編譯器,將變量存儲在程序的靜態存儲區而非棧上空間,靜態數據成員按定義出現的先后順序依次初始化,注意靜態成員嵌套時,要保證所嵌套的成員已經初始化了。消除時的順序是初始化的反順序。
優勢
可以節省內存,因為它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。靜態數據成員的值對每個對象都是一樣,但它的值是可以更新的。只要對靜態數據成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時間效率。
C/C++ 中static的作用
- 在修飾變量的時候,static 修飾的靜態局部變量只執行初始化一次,而且延長了局部變量的生命周期,直到程序運行結束以后才釋放。
- static 修飾全局變量的時候,這個全局變量只能在本文件中訪問,不能在其它文件中訪問,即便是 extern 外部聲明也不可以。
- static 修飾一個函數,則這個函數的只能在本文件中調用,不能被其他文件調用。static 修飾的變量存放在全局數據區的靜態變量區,包括全局靜態變量和局部靜態變量,都在全局數據區分配內存。初始化的時候自動初始化為 0。
- 不想被釋放的時候,可以使用static修飾。比如修飾函數中存放在棧空間的數組。如果不想讓這個數組在函數調用結束釋放可以使用 static 修飾。
- 考慮到數據安全性(當程序想要使用全局變量的時候應該先考慮使用 static)。
面向過程中的static
靜態全局變量
靜態全局變量在全局數據區分配內存,未經初始化的會被程序自動初始化為0(自動變量的值是隨機的,除非它被顯式初始化),在聲明它的整個文件都是可見的,而在文件之外是不可見的;
優點:靜態全局變量不能被其它文件所用;其它文件中可以定義相同名字的變量,不會發生沖突。
全局變量和全局靜態變量的區別
- 全局變量是不顯式用 static 修飾的全局變量,全局變量默認是有外部鏈接性的,作用域是整個工程,在一個文件內定義的全局變量,在另一個文件中,通過 extern 全局變量名的聲明,就可以使用全局變量。
- 全局靜態變量是顯式用 static 修飾的全局變量,作用域是聲明此變量所在的文件,其他的文件即使用 extern 聲明也不能使用。
靜態局部變量
靜態局部變量在全局數據區分配內存,在程序執行到該對象的聲明處時被首次初始化,即以后的函數調用不再進行初始化,一般在聲明處初始化,如果沒有顯式初始化,會被程序自動初始化為0,它始終駐留在全局數據區,直到程序運行結束。但其作用域為局部作用域,當定義它的函數或語句塊結束時,其作用域隨之結束。
面向對象中的static
靜態數據成員
對于非靜態數據成員,每個類對象都有自己的拷貝。而靜態數據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態數據成員在程序中也只有一份拷貝,由該類型的所有對象共享訪問。也就是說,靜態數據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態數據成員只分配一次內存,供所有對象共用。所以,靜態數據成員的值對每個對象都是一樣的,它的值可以更新;
- 在類內數據成員前加上 static 關鍵字,即為靜態數據成員
- 對于類靜態數據成員,無論有多少個該類的對象,該靜態數據成員在內存中只有一份拷貝(其他普通數據成員,每個類對象都有自己的內存拷貝),該靜態數據成員由所有該類對象共享
- 靜態數據成員存儲在全局數據區,在定義時分配存儲空間,程序運行結束時銷毀
- 靜態數據成員不能再類中定義和初始化,只能在類中聲明,在類外進行定義和初始化,默認初始化為0
- 靜態數據成員的初始化為 <類型名> <類名>::<變量名> = <值>
- 靜態數據成員遵從 public private protected 訪問規則
- 靜態數據成員可以直接使用類名加作用域運算符(::)直接訪問 <類名>::<變量名>(訪問規則允許的情況下)
靜態成員函數
與靜態數據成員一樣,我們也可以創建一個靜態成員函數,它為類的全部服務而不是為某一個類的具體對象服務。靜態成員函數與靜態數據成員一樣,都是類的內部實現,屬于類定義的一部分。普通的成員函數一般都隱含了一個this指針,this指針指向類的對象本身,因為普通成員函數總是具體的屬于某個類的具體對象的。通常情況下,this是缺省的。如函數fn()實際上是this->fn()。但是與普通函數相比,靜態成員函數由于不是與任何的對象相聯系,因此它不具有this指針。從這個意義上講,它無法訪問屬于類對象的非靜態數據成員,也無法訪問非靜態成員函數,它只能調用其余的靜態成員函數。
- 出現在類體外的函數定義不能指定關鍵字static;
- 靜態成員之間可以相互訪問,包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數;
- 非靜態成員函數可以任意地訪問靜態成員函數和靜態數據成員;
- 靜態成員函數不能訪問非靜態成員函數和非靜態數據成員,因為靜態成員函數屬于整個類,在類實例化對象之前就已經分配空間了,而類的非靜態成員必須在類實例化對象后才有內存空間;
- 由于沒有this指針的額外開銷,因此靜態成員函數與類的全局函數相比速度上會有少許的增長;
- 調用靜態成員函數,可以用成員訪問操作符(.)和(->)為一個類的對象或指向類對象的指針調用靜態成員函數,也可以直接使用如下格式:
<類名>::<靜態成員函數名>(<參數表>)
調用類的靜態成員函數。
注意:不能將靜態成員函數定義為虛函數。

浙公網安備 33010602011771號