靜態成員變數 Static Member Variable

內部連結 那篇文中,我們講到如果要將一個全局變數改為擁有內部連結,我們可以使用關鍵字 static

在類別中,我們有時候也會看到 static 關鍵字,但意義卻有一些些不一樣。

讓我們一起來看看吧!

什麼是靜態成員變數

我們直接來用實例看看什麼是靜態成員變數,先來看看這個例子:

class Object
{
public:
    int value{0};
};

int main() {
    Object obj1;
    Object obj2;
    
    obj1.value = 1;
    obj2.value = 2;
    
    std::cout << obj1.value << std::endl;
    std::cout << obj2.value << std::endl;

    return 0;
}

這個一個很基本的類別例子。

在這個例子中,我們可以看到有一個類別 Object,我們分別創建兩個實體物件 obj1obj2

兩個物件都有特徵資料 value,我們把它印出來後會看到這樣的輸出

1
2
靜態成員變數

接著我們將 value 改為靜態成員變數:

class Object
{
public:
    static int value;
};

// 初始化靜態成員變數
int Object::value = 1;

int main() {
    Object obj1;
    Object obj2;
    
    obj1.value = 1;
    obj2.value = 2;
    
    std::cout << obj1.value << std::endl;
    std::cout << obj2.value << std::endl;

    return 0;
}

執行程式後,我們可以看到輸出變成這樣了:

2
2

這是為什麼?

因為靜態成員變數 Static Member Variable,是所有實體物件所「共享」的!

我們來看看下面這張圖就可以理解:

左:一般的成員變數。每個實體物件都各自擁有自己的特徵,改變其中一個物件的特徵值並不會影響到其他物件的特徵值。

右:靜態成員變數。靜態成員變數是屬於類別的,因此只有一個,而所有物件都共享這一個變數。

這也說明了為什麼在第二個例子中,更改了 obj2 的變數值,obj1 的值也會一起被改動。

取得靜態成員變數

在上面的例子中,我們看到可以利用物件去取得靜態成員變數。

但是我們也可以透過範圍解析運算子 :: 來取得,比如說:

class Object
{
public:
    static int value;
};

int Object::value = 1;

int main() {
    std::cout << Object::value << std::endl;

    return 0;
}

誒有發現嗎?我們居然在連一個物件都沒有被創建之前就可以取得靜態成員變數了!

想一想其實很合理的,就像前面說的,靜態成員變數是屬於類別的。因此只要類別存在,這個變數就會存在,不需要一個實體物件。

也就是說,這個變數在程式一開始執行時就存在,直到程式終止時才被銷毀。

我們也可以把靜態成員變數想像成一種全局變數,只是它的範圍在類別中。

初始化靜態成員變數

在一開始的例子中,我們可以看到靜態成員變數 value 並不是在一開始就被初始化,而是在類別的外面被初始化。

像是這樣:

int Object::value = 1;

這是因為像前面說的,靜態成員變數被看作是一種全局變數,也因此我們必須在類別的外面去定義並初始化。

如果你真的想要在類別裡面去初始化靜態成員變數,你可以利用關鍵字 const,比如說:

class Object
{
public:
    // 利用 const 在類別內初始化
    static const int value{1};
};

int main() {
    std::cout << Object::value << std::endl;

    return 0;
}

這是因為 const 會需要在程式編譯期就知道初始值。

實際範例

那麼靜態成員變數可以被如何應用呢?

一個最常被應用的例子就是當你想要紀錄創建的物件數量時,靜態成員變數就非常好用!

class Student
{
private:
    int m_id;
public:
    static int id;
    
    int getID()
    {
        id++;
        m_id = id;
        return m_id;
    }
};

int Student::id = 0;

int main() {
    Student A;
    Student B;
    Student C;
    
    std::cout << "學生 A ID: " << A.getID() << std::endl;
    std::cout << "學生 B ID: " << B.getID() << std::endl;
    std::cout << "學生 C ID: " << C.getID() << std::endl;

    return 0;
}

這個例子就紀錄了學生的數量,並根據數量給予不同的 ID。

這樣的輸出就會是:

學生 A ID: 1
學生 B ID: 2
學生 C ID: 3

總結

這章我們了解了什麼是靜態成員變數 Static Member Variable,它的作用以及實際例子。

其實還有另一個靜態成員,叫做靜態成員函數 Static Member Function,下一章就會來介紹他!

那就希望有讓你們學到東西啦!