C++避坑---關(guān)鍵字static的使用及注意事項

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、constvolatile 的。靜態(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)注一下

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容