首頁 > 軟體

C++的靜態成員變數和靜態成員函數你瞭解多少

2022-02-18 16:00:17

靜態成員變數

這裡先參照GeeksforGeeks的一段內容:

Static data members are class members that are declared using static keywords. A static member has certain special characteristics. These are:

  • Only one copy of that member is created for the entire class and is shared by all the objects of that class, no matter how many objects are created.
  • It is initialized before any object of this class is being created, even before main starts.
  • It is visible only within the class, but its lifetime is the entire program

語法: static data_type data_member_name;

靜態變數在任何類物件建立前初始化

我們看程式碼範例,一碼勝千言

#include <iostream>
using namespace std;
class A {
public:
    A() {
        cout << "A constructed" << endl;
    }
};
class B {
    static A a;
public:
    B() {
        cout << "B constructed" << endl;
    }
};
int main() {
    B b;
    return 0;
}
// output
B constructed

我們看到B類有一個靜態成員A類,但是在建立B的物件時並沒有呼叫A的建構函式,原因很簡單,即在類B中僅僅宣告(declare)了靜態類A,但沒有在類外定義(define)它。 如果我們在靜態成員變數定義前使用它,那麼會編譯報錯,這和程式碼塊中的靜態變數不同,程式碼塊中的靜態變數會有常數初始化的過程,程式碼範例如下。

#include <iostream>
using namespace std;
class A {
public:
    int x;
    A() {
        cout << "A constructed" << endl;
    }
};
class B {
    static A a;
public:
    B() {
        cout << "B constructed" << endl;
    }
    static A getA() {return a;}
};
int main() {
    B b;
    // A a = b.getA(); // ERROR Compiler Error: undefined reference to `B::a' 
    static int n;
    cout << n << endl; // ok 0
    return 0;
}

定義靜態成員變數

我們在類內定義靜態成員變數時需要 static,在類外定義鏡頭成員變數時不用 static,語法如下。

class X { static int n; }; // declaration (uses 'static')
int X::n = 1;              // definition (does not use 'static')

這裡需要注意幾點:

  • const靜態成員變數無法在類內初始化
  • 靜態成員變數只能在方法外定義,且一定要定義完才能對起參照。

我們考慮下為什麼不能在宣告中初始化靜態變數,這是因為宣告描述來如何分配記憶體,但不分配記憶體。這裡我們還是使用上面程式碼的例子來說明。

using namespace std;
class A {
public:
    int x;
    A() { cout << "A's constructor called " << endl; }
};
class B {
    static A a;
public:
    B() { cout << "B's constructor called " << endl; }
    static A getA() { return a; }
};
A B::a; // definition of a
int main() {
    B b1, b2, b3;
    A a = b1.getA();
    cout << a.x << endl; // 0
    return 0;
}

output

A's constructor called 
B's constructor called 
B's constructor called 
B's constructor called 
0

從上述結果我們也可以看出來靜態成員變數確實在建立類物件之前初始化。

使用靜態成員變數

有兩種方法可以參照靜態成員變數,<類物件名>.<靜態資料成員名> 或 <類型別名>::<靜態資料成員名>

To refer to a static member m of class T, two forms may be used: qualified name T::m or member access expression E.m or E->m, where E is an expression that evaluates to T or T* respectively. When in the same class scope, the qualification is unnecessary:

struct X
{
    static void f(); // declaration
    static int n;    // declaration
};
X g() { return X(); } // some function returning X
void f()
{
    X::f();  // X::f is a qualified name of static member function
    g().f(); // g().f is member access expression referring to a static member function
}
int X::n = 7; // definition
void X::f() // definition 
{ 
    n = 1; // X::n is accessible as just n in this scope
}

類物件共用靜態成員

靜態類成員有一個特點:無論建立了多少個物件,程式都只建立一個靜態類變數副本。也就是說,類的所有物件共用同一個靜態成員。靜態資料成員和普通資料成員一樣遵從public,protected,private存取規則;

接下來看另一個程式碼範例

#include <iostream>
using namespace std;
class A {
public:
    static int x;
    int y;
    static void f() {
        // y++; Error invalid use of member 'y' in static member function
        x++;
        cout << "A static function, x = " << x << endl;
    }
};
int A::x;
int main() {
    A a;
    cout << "x = " << A::x << endl;
    cout << "x = " << a.x << endl;
    A::f();
    a.f();
    return 0;
}

output

x = 0
x = 0
A static function, x = 1
A static function, x = 2

const constexpr

C++提供了多種在類中定義常數的方式,其中比較常用的有 constconstexprenum

class X
{
	// method1 const
    const static int n = 1;
    const static int m{2}; // since C++11
    const static int k; // ok
    // method2 enum
    enum {Month=12};
    // method3 constexpr
    constexpr static int arr[] = { 1, 2, 3 };        // OK
    constexpr static std::complex<double> n = {1,2}; // OK
    constexpr static int k; // Error: constexpr static requires an initializer
};
const int X::k = 3;

其中注意:

1.使用 enum 時並不會建立資料成員,即所有的物件中都不包括列舉,另外Month知識一個符號名稱,在作用於為整個類的程式碼中遇到它是,編譯器將用12來替代它。而且只能是整數。

2.使用 constexpr 來建立類常數時,一定要給其定義,不能只是宣告,而const可以只是宣告,不用給出定義。 

靜態成員函數

#include <iostream>
using namespace std;
class Person {
public:
    Person() {};
    Person(char *name, int age);
    void show();
    static int getTotal();
private:
    static int m_total;
    char *m_name;
    int m_age;
};
Person::Person(char *name, int age) : m_name(name), m_age(age) {
    m_total++;
}
void Person::show() {
    cout << m_name << "的年齡是" << m_age << ", 總人數是" << m_total << endl;
}
int Person::getTotal() {
    return m_total;
}
// 一定要先初始化
int Person::m_total = 0;
int main() {
    Person *p1 = new Person("Alice", 18);
    Person *p2 = new Person("Bob", 18);
    p1->show();
    p2->show();
    int total1 = Person::getTotal();
    int total2 = p1->getTotal();
    cout << "total1 = " << total1 << ", total2 = " << total2 << endl;
    return 0;
}

靜態成員函數與普通成員函數的根本區別在於:普通成員函數有 this 指標,可以存取類中的任意成員;而靜態成員函數沒有 this 指標,只能存取靜態成員(包括靜態成員變數和靜態成員函數)。這裡要注意的是普通的成員函數也能存取靜態成員變數。這一點上和Java中的static用法很像。

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!      


IT145.com E-mail:sddin#qq.com