天天看點

c++靜态成員函數和靜态變量

一般情況下,如果有N個同類的對象,那麼每一個對象都分别有自己的成員變量,不同對象的成員變量各自有值,互不相幹。但是有時我們希望有某一個或幾個成員變量為所有對象共有,這樣可以實作資料共享。

可以使用全局變量來達到共享資料的目的。例如在一個程式檔案中有多個函數,每一個函數都可以改變全局變量的值,全局變量的值為各函數共享。但是用全局變量的安全性得不到保證,由于在各處都可以自由地修改全局變量的值,很有可能偶然失誤,全局變量的值就被修改,導緻程式的失敗。是以在實際開發中很少使用全局變量。

如果想在同類的多個對象之間實作資料共享,也不要用全局變量,那麼可以使用靜态成員變量。

static靜态成員變量靜态成員變量是一種特殊的成員變量,它以關鍵字 static 開頭。例如:

class Student{

private:

    char *name;

    int age;

    float score;

    static int num;  //将num定義為靜态成員變量

public:

    Student(char *, int, float);

    void say();

}; 這段代碼聲明了一個靜态成員變量 num,用來統計學生的人數。

static 成員變量屬于類,不屬于某個具體的對象,這就意味着,即使建立多個對象,也隻為 num 配置設定一份記憶體,所有對象使用的都是這份記憶體中的資料。當某個對象修改了 num,也會影響到其他對象。

static 成員變量必須先初始化才能使用,否則連結錯誤。例如:

int Student::num;  //初始化      

也可以在初始化時賦初值:

int Student::num = 10;  //初始化同時指派      

初始化時可以不加 static,但必須要有資料類型。被 private、protected、public 修飾的 static 成員變量都可以用這種方式初始化。

注意:static 成員變量的記憶體空間既不是在聲明類時配置設定,也不是在建立對象時配置設定,而是在初始化時配置設定。

static 成員變量既可以通過對象來通路,也可以通過類來通路。通過類來通路的形式為:

類名::成員變量;

例如: //通過類來通路

Student::num = 10;

//通過對象來通路

Student stu;

stu.num = 10; 這兩種方式是等效的。

注意:static 成員變量與對象無關,不占用對象的記憶體,而是在所有對象之外開辟記憶體,即使不建立對象也可以通路。

下面來看一個完整的例子: #include <iostream>

using namespace std;

class Student{

private:

    char *name;

    int age;

    float score;

    static int num;  //将num定義為靜态成員變量

public:

    Student(char *, int, float);

    void say();

};

int Student::num = 0;  //初始化靜态成員變量

Student::Student(char *name, int age, float score){

    this->name = name;

    this->age = age;

    this->score = score;

    num++;

}

void Student::say(){

    //在普通成員函數中可以通路靜态成員變量

    cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(目前共"<<num<<"名學生)"<<endl;

}

int main(){

    //使用匿名對象

    (new Student("小明", 15, 90))->say();

    (new Student("李磊", 16, 80))->say();

    (new Student("張華", 16, 99))->say();

    (new Student("王康", 14, 60))->say();

    return 0;

} 運作結果:

小明的年齡是 15,成績是 90(目前共1名學生)

李磊的年齡是 16,成績是 80(目前共2名學生)

張華的年齡是 16,成績是 99(目前共3名學生)

王康的年齡是 14,成績是 60(目前共4名學生)

本例中将 num 聲明為靜态成員變量,每次建立對象時,會調用構造函數,将 num 的值加 1。之是以使用匿名對象,是因為每次建立對象後隻會使用它的 say 函數,不再進行其他操作。不過請注意,使用匿名對象有記憶體洩露的風險。

關于靜态資料成員的幾點說明:

1) 一個類中可以有一個或多個靜态成員變量,所有的對象都共享這些靜态成員變量,都可以引用它。

2) static 成員變量和普通 static 變量一樣,編譯時在靜态資料區配置設定記憶體,到程式結束時才釋放。這就意味着,static 成員變量不随對象的建立而配置設定記憶體,也不随對象的銷毀而釋放記憶體。而普通成員變量在對象建立時配置設定記憶體,在對象銷毀時釋放記憶體。

3) 靜态成員變量必須初始化,而且隻能在類體外進行。例如:

int Student::num = 10;      

初始化時可以賦初值,也可以不指派。如果不指派,那麼會被預設初始化,一般是 0。靜态資料區的變量都有預設的初始值,而動态資料區(堆區、棧區)的變量預設是垃圾值。

4) 靜态成員變量既可以通過對象名通路,也可以通過類名通路,但要遵循 private、protected 和 public 關鍵字的通路權限限制。當通過對象名通路時,對于不同的對象,通路的是同一份記憶體。

static靜态成員函數

在類中,static 除了聲明靜态成員變量,還可以聲明靜态成員函數。普通成員函數可以通路所有成員變量,而靜态成員函數隻能通路靜态成員變量。

我們知道,當調用一個對象的成員函數(非靜态成員函數)時,系統會把目前對象的起始位址賦給 this 指針。而靜态成員函數并不屬于某一對象,它與任何對象都無關,是以靜态成員函數沒有 this 指針。既然它沒有指向某一對象,就無法對該對象中的非靜态成員進行通路。

可以說,靜态成員函數與非靜态成員函數的根本差別是:非靜态成員函數有 this 指針,而靜态成員函數沒有 this 指針。由此決定了靜态成員函數不能通路本類中的非靜态成員。

靜态成員函數可以直接引用本類中的靜态資料成員,因為靜态成員同樣是屬于類的,可以直接引用。在C++程式中,靜态成員函數主要用來通路靜态資料成員,而不通路非靜态成員。 如果要在類外調用 public 屬性的靜态成員函數,要用類名和域解析符“::”。如:

Student::getNum();      

當然也可以通過對象名調用靜态成員函數,如:

stu.getNum();      

下面是一個完整的例子,通過靜态成員函數獲得學生的平均成績: #include <iostream>

using namespace std;

class Student{

private:

    char *name;

    int age;

    float score;

    static int num;  //學生人數

    static float total;  //總分

public:

    Student(char *, int, float);

    void say();

    static float getAverage();  //靜态成員函數,用來獲得平均成績

};

int Student::num = 0;

float Student::total = 0;

Student::Student(char *name, int age, float score){

    this->name = name;

    this->age = age;

    this->score = score;

    num++;

    total += score;

}

void Student::say(){

    cout<<name<<"的年齡是 "<<age<<",成績是 "<<score<<"(目前共"<<num<<"名學生)"<<endl;

}

float Student::getAverage(){

    return total / num;

}

int main(){

    (new Student("小明", 15, 90))->say();

    (new Student("李磊", 16, 80))->say();

    (new Student("張華", 16, 99))->say();

    (new Student("王康", 14, 60))->say();

    cout<<"平均成績為 "<<Student::getAverage()<<endl;

    return 0;

}