C++ static詳解,類中的static用法說明
C++static詳解,類中static用法
static特點:用來控制存儲方式和可見性
① 存儲空間:靜態(tài)存儲區(qū)(控制變量的存儲方式)
靜態(tài)變量存儲在靜態(tài)存儲區(qū)(存儲在靜態(tài)存儲區(qū)的變量,如果不顯式地對其進行初始化,系統(tǒng)會將其初始化為0),在程序執(zhí)行期間,對應的存儲空間不會釋放,一直到程序結束才會釋放。
static控制變量的存儲方式體現(xiàn)在局部變量上。局部變量存儲在動態(tài)存儲區(qū),在局部變量定義前加上static,該局部變量就變成了局部靜態(tài)變量,局部靜態(tài)變量存儲在靜態(tài)存儲區(qū),即使函數(shù)調用結束,它占用的存儲空間也不會釋放,但其他函數(shù)不能引用該局部靜態(tài)變量。當下一次調用該函數(shù)時,不會再次對該局部靜態(tài)變量進行初始化,它的值是上一次函數(shù)調用結束時的值。
對全局變量而言,存儲方式沒有什么改變,因為全局變量和全局靜態(tài)變量都存儲在靜態(tài)存儲區(qū)。
② 作用域:(控制變量、函數(shù)的可見性)
static控制變量的可見性體現(xiàn)在全局靜態(tài)變量和靜態(tài)函數(shù)上。
全局變量默認具有外部鏈接性,作用域是整個工程。使用static關鍵字可以限制全局變量的作用域,全局靜態(tài)變量的作用域僅限本文件,它對在其他文件不可見,也就是說不能在其他文件中引用該全局靜態(tài)變量,但其他文件中可以定義和它名字相同的變量,不會發(fā)生沖突。
在函數(shù)的返回類型前加上static關鍵字,函數(shù)即被定義為靜態(tài)函數(shù)。靜態(tài)函數(shù)與普通函數(shù)的不同在于,它只能在聲明它的文件當中可見,不能被其它文件使用,其它文件中可以定義相同名字的函數(shù),不會發(fā)生沖突。
局部靜態(tài)變量的作用域與局部變量的作用域相同,其作用域都是從定義開始到函數(shù)或程序塊結束為止。
類中的static關鍵字
在類中聲明static變量或者函數(shù)時,初始化時使用作用域運算符(::)來標明它所屬類,靜態(tài)成員是類的成員(所有對象中共享的成員),而不是某一個對象的成員。
① 靜態(tài)數(shù)據成員
在類內數(shù)據成員的聲明前加上關鍵字static,該數(shù)據成員就是類內的靜態(tài)數(shù)據成員。
靜態(tài)數(shù)據成員和普通數(shù)據成員一樣遵從public,protected,private訪問規(guī)則。
對于非靜態(tài)數(shù)據成員,每個對象都有自己的拷貝。而靜態(tài)數(shù)據成員被當作是類的成員。無論這個類的對象被定義了多少個,靜態(tài)數(shù)據成員在程序中也只有一份拷貝,由該類的所有對象共享訪問。也就是說,靜態(tài)數(shù)據成員是該類的所有對象所共有的。對該類的多個對象來說,靜態(tài)數(shù)據成員只會被分配一次內存,供所有對象共用。所以,靜態(tài)數(shù)據成員的值對每個對象都是一樣的,它的值可以更新。
因為靜態(tài)數(shù)據成員在全局數(shù)據區(qū)分配內存,屬于本類的所有對象共享,所以,它不屬于特定的對象,在沒有產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它。
同全局變量相比,使用靜態(tài)數(shù)據成員有兩個優(yōu)勢:
靜態(tài)數(shù)據成員沒有進入程序的全局名字空間,因此不存在與程序中其它全局名字沖突的可能性;
可以實現(xiàn)信息隱藏。靜態(tài)數(shù)據成員可以是private成員,而全局變量不能;
② 靜態(tài)成員函數(shù)
與靜態(tài)數(shù)據成員一樣,我們也可以創(chuàng)建一個靜態(tài)成員函數(shù),它為類的全部服務而不是為某一個類的具體對象服務。靜態(tài)成員函數(shù)與靜態(tài)數(shù)據成員一樣,都是類的內部實現(xiàn),屬于類定義的一部分。普通的成員函數(shù)一般都隱含了一個this指針,this指針指向類的對象本身,因為普通成員函數(shù)總是具體的屬于某個類的具體對象的。通常情況下,this是缺省的,如函數(shù)fun()實際上是this->fun()。但是與普通函數(shù)相比,靜態(tài)成員函數(shù)由于不與任何的對象相聯(lián)系,因此它不具有this指針。
非靜態(tài)成員函數(shù)可以任意地訪問靜態(tài)成員函數(shù)和靜態(tài)數(shù)據成員;
靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員函數(shù)和非靜態(tài)數(shù)據成員;靜態(tài)成員之間可以相互訪問,包括靜態(tài)成員函數(shù)訪問靜態(tài)數(shù)據成員和訪問靜態(tài)成員函數(shù);
什么時候用static?
需要一個數(shù)據對象為整個類而非某個對象服務,同時又力求不破壞類的封裝性,即要求此成員隱藏在類的內部,對外不可見。
為什么要引入static?
函數(shù)內部定義的變量,在程序執(zhí)行到它的定義處時,編譯器為它在棧上分配空間,大家知道,函數(shù)在棧上分配的空間在此函數(shù)執(zhí)行結束時會釋放掉,這樣就產生了一個問題:如果想將函數(shù)中此變量的值保存至下一次調用時,如何實現(xiàn)?最容易想到的方法是定義一個全局的變量,但定義為一個全局變量有許多缺點,最明顯的缺點是破壞了此變量的訪問范圍(使得在此函數(shù)中定義的變量,不僅僅受此函數(shù)控制)。
c++中static總結
經過static修飾的變量,存儲在內存的全局靜態(tài)區(qū)。且被static修飾的變量只能在本模塊的所有函數(shù)引用。
內存中的存儲區(qū)域如下:
- 堆區(qū):是由程序員手動申請(new)與釋放(delete)的內存區(qū)域。從低地址向高地址申請;內存空間大、存儲地址不連續(xù),一般是鏈式的;速度較慢。
- 棧區(qū):由編譯器自動分配和釋放,主要存儲 函數(shù)的參數(shù)值、函數(shù)內部的變量的值、函數(shù)調用的空間。從高地址向低地址申請;容量有限;速度較快;存儲地址連續(xù),會溢出。
- 代碼區(qū):又叫文本段(.text),存放著程序的機器代碼,可執(zhí)行指令就是存儲在這里的,這里的代碼是只讀的。
- 全局區(qū)(靜態(tài)區(qū)):全局變量和靜態(tài)變量是存儲在這里的。初始化的全局變量和靜態(tài)變量在一塊區(qū)域(.data),未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域(.bbs)。系統(tǒng)結束后由系統(tǒng)釋放。
- 常量區(qū):常量字符串放在這里,程序結束后,由系統(tǒng)進行釋放。
1. 概念
static的用法主要體現(xiàn)在兩個方面:面向過程中的static和面向對象中的static。
面向過程的static主要包括靜態(tài)全局變量、靜態(tài)局部變量和靜態(tài)函數(shù)。
面向對象的static主要包括靜態(tài)成員變量、靜態(tài)成員函數(shù)。
2. 面向過程的static
2.1 靜態(tài)全局變量
全局變量前添加static關鍵字,則該變量稱為靜態(tài)全局變量。
#include <iostream> #include <stdio.h> ? static int a = 10; ? void Func() { ?? ?a++; } ? int main() { ?? ?printf("a = %d\n", a);//輸出:10 ?? ?Func();//輸出:10 ?? ?printf("a = %d\n", a);//輸出:11 ? ?? ?system("pause"); ? ? return 0; }
特點:
1)在全局數(shù)據中的變量如果沒有顯示的初始化會自動被程序初始化為0(這個特性非靜態(tài)全局變量也有),而在函數(shù)體內聲明的變量如果不顯示初始化則會使一個隨機值;
2)靜態(tài)全局變量在聲明它的整個文件中都是可見的,而在文件之外是不可見的;
3)靜態(tài)全局變量在全局數(shù)據區(qū)分配內存;
4)其他文件中可以定義同名int型變量a,不會沖突;
如果將static去掉,具有以下特點:
1)全局變量默認是有外部連接性的,其作用域是整個工程,在一個文件內定義的全局變量可以通過包含其所在頭文件或顯示調用 extern關鍵字修飾全局變量的變量名聲明來引用;
2)靜態(tài)全局變量是顯示調用static修飾的全局變量,其作用域只在聲明此變量的文件中,其他文件即使使用extern關鍵字修飾其聲明也不可使用;
2.2 靜態(tài)局部變量
局部變量前添加static關鍵字,則該變量稱為靜態(tài)局部變量。
#include <iostream> #include <stdio.h> ? void Func() { ?? ?static int a = 5; ?? ?printf("a = %d\n", a); ?? ?a++; } ? int main() { ?? ?for (int i = 0; i < 5; i++) ?? ?{ ?? ??? ?Func(); ?//打印結果:5 6 7 8 9 ?? ?} ? ?? ?system("pause"); ? ? return 0; }
通常,在一個函數(shù)作用域內定義一個變量,每次運行到該函數(shù)時,系統(tǒng)會給局部變量分配內存。當函數(shù)結束時,該變量的內存會被系統(tǒng)回收至棧內存當中。
特點:
1)內存存放在程序的全局數(shù)據區(qū)中;
2)靜態(tài)局部變量在程序執(zhí)行到該對象聲明時,會被首次初始化。其后運行到該對象的聲明時,不會再次初始化,這也是為什么上面程序測試函數(shù)每次輸出的值都是遞增的原因(只會被初始化一次);
3)如果靜態(tài)局部變量沒有被顯式初始化,則其值會自動被系統(tǒng)初始化為0;
4)局部靜態(tài)變量不能被其作用域之外的其他模塊調用,其調用范圍僅限于聲明該變量的函數(shù)作用域當中;
2.3 靜態(tài)函數(shù)
函數(shù)返回類型前添加static關鍵字,則該變量稱為靜態(tài)函數(shù)。
#include <iostream> #include <stdio.h> ? static void Func() { ?? ?printf("This is a static function\n"); } ? int main() { ?? ?Func();? ?? ? ?? ?system("pause"); ? ? return 0; }
特點:
1)作用域只在聲明它的文件當中,不能被其他文件引用,其他文件可以定義同名的全局函數(shù);
2)其他文件想要調用本文件的靜態(tài)函數(shù),需要顯示的調用extern關鍵字修飾其聲明;
3. 面向對象中的static
3.1 靜態(tài)成員變量
#include <iostream> #include <stdio.h> using namespace std; ? class Test { public: ?? ?Test(int a, int b, int c) :? ?? ??? ?m_a(a), ?? ??? ?m_b(b), ?? ??? ?m_c(c) ?? ?{ ?? ??? ?m = a + b + c; ?? ?} ? ?? ?void Show() ?? ?{ ?? ??? ?cout << "m = " << m << endl; ?? ?} ? private: ?? ?int m_a, m_b, m_c; ?? ?static int m; }; ? int Test::m = 0; //初始化靜態(tài)數(shù)據成員 ? int main() { ?? ?Test ClassA(1, 1, 1); ?? ?ClassA.Show(); ?//輸出: 3 ?? ?Test ClassB(3, 3, 3); ?? ?ClassB.Show(); //輸出: 9 ?? ?ClassA.Show(); //輸出: 9 ? ?? ?system("pause"); ?? ?return 0; }
特點:
1)靜態(tài)數(shù)據成員的服務對象并非是單個類實例化的對象,而是所有類實例化的對象(這點可以用于設計模式中的單例模式實現(xiàn));
2)靜態(tài)數(shù)據成員必須顯式的初始化分配內存,在其包含類沒有任何實例化之前已經有內存分配;
3)靜態(tài)數(shù)據成員與其他成員一樣,遵從public,protected,private的訪問規(guī)則;
4)靜態(tài)數(shù)據成員內存存儲在全局數(shù)據區(qū),只隨著進程的消亡而消亡;
優(yōu)勢:
1)靜態(tài)數(shù)據成員不進入程序全局命名空間,不會與其他全局名稱的同名同類型變量沖突;
2)靜態(tài)數(shù)據成員可以實現(xiàn)C++的封裝特性,由于其遵守類的訪問權限規(guī)則,所以相比全局變量更加靈活;
3.2 靜態(tài)成員函數(shù)
類的成員函數(shù)返回類型之前添加static,此成員函數(shù)為靜態(tài)成員函數(shù)。
#include <iostream> #include <stdio.h> using namespace std; ? class Test { public: ?? ?Test(int a, int b, int c) :? ?? ??? ?m_a(a), ?? ??? ?m_b(b), ?? ??? ?m_c(c) ?? ?{ ?? ??? ?m = a + b + c; ?? ?} ? ?? ?static void Show() ?? ?{ ?? ??? ?cout << "m = " << m << endl; ?? ?} ? private: ?? ?int m_a, m_b, m_c; ?? ?static int m; }; ? int Test::m = 0; //初始化靜態(tài)數(shù)據成員 ? int main() { ?? ?Test ClassA(1, 1, 1); ?? ?ClassA.Show(); ?? ?Test ClassB(3, 3, 3); ?? ?ClassB.Show(); ?? ?ClassA.Show(); ? ? ? Test::Show(); //輸出: 9 ? ?? ?system("pause");? ?? ?return 0; }
特點:
1)靜態(tài)成員函數(shù)比普通成員函數(shù)多了一種調用方式;
2)在沒有實例化的類對象的條件下可以調用類的靜態(tài)成員函數(shù);
3)靜態(tài)成員函數(shù)中沒有隱含的this指針,所以靜態(tài)成員函數(shù)不可以操作類中的非靜態(tài)成員(由于第二條可知,類的非靜態(tài)成員是在類實例化后存在的,而類的成員函數(shù)可以在類沒有實例化的時候調用,故不能操作類的非靜態(tài)成員);
4. 小結
1)靜態(tài)數(shù)據成員都是靜態(tài)存儲的,所以必須在main函數(shù)之前顯示的對其進行初始化;
2)不能再頭文件中聲明靜態(tài)全局變量,原因是可能是產生了多個同名的靜態(tài)數(shù)據;
3)不能將靜態(tài)成員函數(shù)定義為虛函數(shù);
4)靜態(tài)成員函數(shù)沒有this指針;
5)static縮短了子類對父類靜態(tài)成員訪問的時間,相對來說節(jié)省了內存空間;
6)如果不想在子類中操作父類的靜態(tài)成員,則可以在子類中定義一個同名的static成員。這樣既可覆蓋父類中的靜態(tài)成員,并且根據C++的多態(tài)性變量命名規(guī)則,這樣做是安全的;
7)靜態(tài)成員聲明在類中,操作在其外部,所以對其取地址操作就跟取普通成員的操作略有不同。靜態(tài)變量地址是指向其數(shù)據類型的指針,函數(shù)地址則是一個類型為nonmember的函數(shù)指針;
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。