static根據上下文語意有兩種含義,一種是在類和結構體內,另一種時類在結構體外。
類外的static在鏈接階段是局部的,它只對它的編譯單元(.obj)可見,而類內的static表示這個變量將在類內與所有實例共享
Static.cpp
static int s_Variable=5;
main.cpp
#include<iostream> int s_Variable=10; int main() { std::cout<<s_Variable<<std::endl; std::cin.get(); }
程序的運行結果是打印10,如果將Static.cpp中的static去掉,直接變成int聲明變量s_Variable,那么在鏈接時會報錯,因為s_Variable已經在另一個編譯單元中被定義了,兩個全局變量的名字不能一樣,改正方法是使用引用
Static.cpp
int s_Variable=5;
main.cpp
#include<iostream> extern int s_Variable; int main() { std::cout<<s_Variable<<std::endl; std::cin.get(); }
extern的意思是在另外的編譯單元中尋找定義,也叫外部鏈接,此時運行可以看到打印結果是5。加上static有些類似於在類中聲明私有類型成員,其他的編譯單元(.obj)不能訪問s_Variable,函數也是一樣。
Static.cpp
void function() {}
main.cpp
#include<iostream> int function() {} int main() { std::cin.get(); }
此時編譯會發生錯誤,因為function被重復定義,如果將Static.cpp中的function前面加上static,那么鏈接將不會出錯。
在頭文件中使用靜態變量也是同樣的道理,頭文件相當於在引用頭文件的位置將頭文件的內容復制粘貼,因此頭文件中使用static定義的靜態變量即使在兩個不同的cpp文件中被調用,也不會引起重復定義,因為它相當於在兩個cpp文件中各自建立一個靜態變量,互不干擾。
因此盡量讓全局變量和函數變成靜態類型,除非要在別的cpp文件中調用它
static在類和結構內代表什么
表示類內所有同名變量都代表一個實體,如果這個屍體的值發生了改變,那么類中所有的這個實體都同樣會改變。
#include<iostream> struct Entity{ int x,y; void Print() { std::cout<<x<<","<<y<<std::endl; } }; int main() { Entity e; e.x=2; e.y=3; Entity e1={5,8}; e.Print(); e1.Print(); std::cin.get(); }
打印結果是2,3 5,8
#include<iostream> struct Entity{ static int x,y; void Print() { std::cout<<x<<","<<y<<std::endl; } }; int Entity::x; int Entity::y; int main() { Entity e; e.x=2; e.y=3; Entity e1; e1.x=5; e1.y=8; e.Print(); e1.Print(); std::cin.get(); }
此時的打印結果是兩個5,8,需要注意的是靜態變量無法訪問非靜態變量,如果代碼做如下修改,那么靜態函數Print將無法訪問非靜態的變量x和y
#include<iostream> struct Entity{ int x,y; static void Print() { std::cout<<x<<","<<y<<std::endl; } }; int main() { Entity e; e.x=2; e.y=3; Entity e1; e1.x=5; e1.y=8; Entity::Print(); std::cin.get(); }
使用訪問命名空間的方法來訪問類/結構體中的靜態成員是因為靜態類型是唯一的,無需通過新建類/結構體來實現訪問。如果做以下修改,可以正常運行
#include<iostream> struct Entity{ int x,y; static void Print() { std::cout<<x<<","<<y<<std::endl; } }; static void Print(Entity e) { std::cout<<e.x<<","<<e.y<<std::endl; } int main() { Entity e; e.x=2; e.y=3; Entity e1; e1.x=5; e1.y=8; Print(e); std::cin.get(); }
本地作用域中(local scope)的靜態變量
需要掌握的變量的生命周期(變量在被刪除之前在內存中存儲多久)和作用域(在哪里可以訪問到這個變量)
局部靜態變量允許我們定義一個生命周期是整個程序的變量,但是他的作用域被限制在當前函數中,其實也不一定是函數,可以在任何作用域中聲明靜態變量,函數中的靜態變量和類中的靜態變量其實沒有很大的區別,他們的生命周期是一樣的,唯一區別是類中的靜態變量可以被類內的任何變量訪問,在函數作用域中聲明的靜態變量,對於函數來說是局部的,就像類的靜態變量對於類來說也是局部的。如果在函數中聲明一個靜態變量,那么在第一次調用函數時,這個靜態變量被創建,后續再次調用此函數時將不會在創建這個變量。
#include<iostream> void func() { int i=0; i++; std::cout<<i<<std::endl; } int main() { func(); func(); func(); std::cin.get(); }
此函數的輸出結果是三個1,如果將int i=0改為靜態變量,那么結果將輸出1,2,3
#include<iostream> void func() { static int i=0; i++; std::cout<<i<<std::endl; } int main() { func(); func(); func(); std::cin.get(); }
將i設在函數外面作為全局變量也是一樣的結果,但是當i作為全局變量時,就意味着我在任何地方都可以調用它,所以會有一些意外的發生。
#include<iostream> static int i=0; void func() { i++; std::cout<<i<<std::endl; } int main() { func(); i=10; func(); func(); std::cin.get(); }
此時輸出結果是1,11,12,如果不想讓程序出現這種效果,此時可以將i變成局部靜態變量
同樣,局部靜態變量可以簡化代碼
#include<iostream> class Singleton{ private: static Singleton* s_Instance; public: static Singleton& get() {return s_Instance;} void Hello() {} }; Singleton *Singleton::s_Instance=NULL; int main() { Singleton::Get().Hello(); std::cin.get(); }
簡化如下
#include<iostream> class Singleton{ public: static Singleton& get() { static Singleton instance; return instance; } void Hello() {} }; int main() { Singleton::Get().Hello(); std::cin.get(); }
如果Singleton instance前面沒有static,因為Singleton是在堆棧上建立的,在運行到結束花括號時被銷毀,函數結束,在返回引用時這將會是個嚴重的錯誤,如果將表示引用的“&”去掉將不會有錯,加上了static將會大大延長了instance的生命周期,每次調用get()的時候后面的調用都會返回到第一次構造的Singleton實例。