static關(guān)鍵字可用于聲明變量、函數(shù)、類數(shù)據(jù)成員和類函數(shù)。其主要影響著它們的生命周期、作用域和存儲位置。
static在普通變量和函數(shù)中的使用
-
static修飾局部變量
默認(rèn)情況下 static修飾后 作用域 函數(shù)內(nèi)部 函數(shù)內(nèi)部 存儲位置 棧 靜態(tài)區(qū) 生命周期 局部(函數(shù)調(diào)用開始,函數(shù)執(zhí)行完成時結(jié)束) 全局(函數(shù)調(diào)用開始,到程序執(zhí)行完成時結(jié)束) 默認(rèn)情況下,局部變量(定義在函數(shù)內(nèi)部的變量)的作用域僅限于函數(shù)內(nèi)部,只有函數(shù)被調(diào)用的時候才被初始化(存儲于??臻g)和使用,函數(shù)執(zhí)行完畢的時候被釋放,而被
static修飾后,其作用域不變,但是存儲位置變?yōu)殪o態(tài)區(qū),且生命周期延長(直到程序運(yùn)行結(jié)束后才被釋放)。#include <iostream> void fun() { int a = 0;//在函數(shù)每次調(diào)用的時候都會重新初始化 static int b = 0;//在函數(shù)第一次調(diào)用的時候只初始化一次 a++; b++; std::cout << "a = " << a << ", b = " << b << std::endl; } int main() { int i = 5; while (i--) { fun(); } return 0; }輸出結(jié)果為:
a = 1, b = 1 a = 1, b = 2 a = 1, b = 3 a = 1, b = 4 a = 1, b = 5 -
static修飾全局變量
默認(rèn)情況下 static修飾后 作用域 整個程序 當(dāng)前文件 存儲位置 靜態(tài)區(qū) 靜態(tài)區(qū) 生命周期 全局 全局 默認(rèn)情況下,全局變量(在所有塊的外部定義變量)的作用域是整個程序,存儲位置為靜態(tài)區(qū),生命周期也與程序的生命周期相同。當(dāng)其被
static修飾后,其存儲位置和生命周期不會改變,但其作用域變?yōu)楫?dāng)前文件。//--------a.cpp-------- int a = 1; static int b = 1; //僅在該文件內(nèi)部可見 //------main.cpp------- #include <iostream> extern int a; // extern int b; int main() { std::cout << a << std::endl; // std::cout << b << std::endl; // 錯誤,導(dǎo)致程序編譯報錯 return 0; } -
static修飾函數(shù)
static修飾函數(shù)的作用與修飾全局變量的作用類似,即將函數(shù)的作用域由整個程序變?yōu)楫?dāng)前文件。//--------a.cpp-------- #include <iostream> void fn1() { std::cout<<"this is non-static func in a.cpp."<<std::endl; } static void fn2() { std::cout<<"this is static func in a.cpp."<<std::endl; } //------main.cpp------- #include <iostream> extern void fn1(); // 使用extern聲明其他文件的fn(),供main.cpp使用 // extern void fn2(); int main() { fn1(); // fn2(); // 錯誤,編譯報錯 return 0; }
static在類中的使用
在類定義中,關(guān)鍵字static聲明不綁定到類的實例對象。static在類中常用的使用場景主要有:
-
修飾類的數(shù)據(jù)成員:即靜態(tài)成員,該成員不關(guān)聯(lián)到任何對象,即使不定義該類的任何對象它也存在,其生存周期大于該類的對象,其作為類的成員,由該類的所有對象共同訪問。靜態(tài)成員存儲于全局?jǐn)?shù)據(jù)區(qū),由于要給靜態(tài)成員分配空間,因此普通的靜態(tài)成員不能在類的聲明中定義。其定義以及使用方法如下:
//普通的靜態(tài)成員 class X { static int n; }; // 聲明(用 'static') int X::n = 1; // 定義(不用 'static') //常量靜態(tài)成員 /*如果整型或枚舉類型的靜態(tài)數(shù)據(jù)成員 被聲明為 const(且非 volatile), 那么它能以其中的每個表達(dá)式均為常量表達(dá)式 的初始化器直接在類定義內(nèi)初始化:*/ struct X { const static int n = 1; const static int m{2}; // C++11 起 const static int k; }; const int X::k = 3; //inline 靜態(tài)數(shù)據(jù)成員 /*靜態(tài)數(shù)據(jù)成員可以聲明為 inline。 inline 靜態(tài)數(shù)據(jù)成員可以在類定義中定義, 而且可以指定初始化器。它不需要類外定義。*/ struct X { inline static int n = 1; // C++17 起 }; -
修飾類的成員函數(shù):即靜態(tài)成員函數(shù),該函數(shù)與靜態(tài)成員一樣,不關(guān)聯(lián)到任何對象。調(diào)用時,它們沒有
this指針。靜態(tài)成員函數(shù)不能是virtual、const或volatile的。靜態(tài)成員函數(shù)的地址可以存儲在常規(guī)的函數(shù)指針中,但不能存儲在成員函數(shù)指針中。其定義及使用方法如下:struct X { static void sf(){}; void f(){}; static int n; // 聲明 }; int X::n = 7; // 定義 X x; x.sf(); // 通過對象進(jìn)行調(diào)用 X::sf(); // 通過類名::函數(shù)名進(jìn)行調(diào)用 void (X::*pf)() = &X::f; // void (X::*psf)() = &X::sf; //錯誤,靜態(tài)成員函數(shù)的地址不能存儲在成員函數(shù)指針中。 void (*psf)() = &X::sf; // 靜態(tài)成員函數(shù)的地址可以存儲在常規(guī)的函數(shù)指針中 (x.*pf)(); (*psf)();
介紹完static的使用方式和方法后,我們通過一個完整的例子,總結(jié)一下其使用時的注意事項,例子如下:
#include <iostream>
using namespace std;
class A {
public:
A(int a) {
a_ = a;
s_sum_ += a;
}
int getSum() { return s_sum_; }
void showA() { cout << "a = " << a_ << ", s_sum_ = " << s_sum_ << endl; }
//static void showA() { cout << "a = " << a_ << ", sum = " << s_sum_ << endl; } //錯誤,靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員和非靜態(tài)成員函數(shù)。
static void showSum() { cout << "s_sum_ = " << s_sum_ << endl; }
private:
int a_;
static int s_sum_;
//static int s_sum_ = 0; // 錯誤,non-const static成員不能在類的內(nèi)部初始化
/*【注:C++17起,inline 靜態(tài)數(shù)據(jù)成員可以在類定義中定義
(即inline static int s_sum_ = 0;),
而且可以指定初始化器。它不需要類外定義】*/
};
int A::s_sum_ = 0; // 初始化 【非const靜態(tài)成員必須在類的外面初始化】
int main() {
A::showSum();
cout << "sizeof(A) = " << sizeof(A) << endl;
A a1(1);
cout << "sizeof(a1) = " << sizeof(a1) << ", s_sum_ = " << a1.getSum() << endl;
a1.showA();
A a2(2);
cout << "sizeof(a2) = " << sizeof(a2) << ", s_sum_ = " << a2.getSum() << endl;
a2.showA();
a1.showSum();
a2.showSum();
A::showSum();
return 0;
}
輸出結(jié)果如下:
s_sum_ = 0
sizeof(A) = 4
sizeof(a1) = 4, s_sum_ = 1
a = 1, s_sum_ = 1
sizeof(a2) = 4, s_sum_ = 3
a = 2, s_sum_ = 3
s_sum_ = 3
s_sum_ = 3
s_sum_ = 3
從上述例子和輸出結(jié)果可以得出:
- 靜態(tài)數(shù)據(jù)成員不關(guān)聯(lián)到類對象,且只初始化一次,單獨(dú)存儲。
- 普通的靜態(tài)成員不能在類的聲明中定義,需要在類的外面單獨(dú)初始化。
- 靜態(tài)成員函數(shù)能夠訪問靜態(tài)成員,但不能訪問非靜態(tài)數(shù)據(jù)成員和非靜態(tài)成員函數(shù)(因為非靜態(tài)數(shù)據(jù)成員和函數(shù)是與類的對象綁定的,但靜態(tài)成員函數(shù)不關(guān)聯(lián)任何對象,且調(diào)用時,它們沒有
this指針。)。
總 結(jié)
在使用關(guān)鍵字static的時候,我們需要注意一下幾個原則:
- 被
static修飾的變量的存儲位置都在靜態(tài)區(qū),且生命周期延長至整個程序運(yùn)行結(jié)束。 -
static具有隔離作用,被static修飾的全局變量和全局函數(shù)的作用域由全局變?yōu)楫?dāng)前文件。 - 靜態(tài)變量雖然具有全局變量的生命周期,但只能作用于自己的作用域。
- 普通靜態(tài)成員需要在類的外面進(jìn)行初始化。
- 靜態(tài)成員函數(shù)不能調(diào)用類的非靜態(tài)成員,包括非靜態(tài)成員和非靜態(tài)成員函數(shù)。
參考文獻(xiàn)
《C++ reference》
文章首發(fā)公眾號:iDoitnow 如果喜歡話,可以關(guān)注一下