1. 程式人生 > >C++ 類的static靜態成員

C++ 類的static靜態成員

復制 app 運算 而不是 公有 隱式 數據類型 開始 全局變量

靜態static

靜態成員的提出是為了解決數據共享的問題。實現共享有許多方法,如:設置全局性的變量或對象是一種方法。但是,全局變量或對象是有局限性的。

在全局變量前,加上關鍵字static該變量就被定義成為了一個靜態全局變量。 該變量只有在本源文件中可見,嚴格講應該為定義之處開始到本文件結束,靜態全局變量不能被其他文件所用。

通常,在函數體內定義一個變量,每當程序運行到該語句時都會給該局部變量分配棧內存。但隨著程序退出函數體,系統就會回收棧內存,局部變量也相應失效。但有時候我們需要在兩次調用之間對變量的值進行保存。通常的想法是定義一個全局變量來實現。但這樣一來,變量已經不再屬於函數本身了不再受函數的控制,給函數的維護帶來不便。靜態局部變量正好可以解決這個問題。靜態局部變量保存在全局數據區,而不是保存在棧中,每次的值保存到下一次調用,直到下次賦新值。

與函數體內的靜態局部變量相似,在類中使用靜態成員變量可實現多個對象之間的數據共享,又不會破壞隱藏的原則,保證了安全性還可以節省內存。定義數據成員為靜態變量,表明此全局數據邏輯上屬於該類。定義成員函數為靜態函數,表明此全局函數邏輯上屬於該類,而且該函數只對靜態數據、全局數據或者參數進行操作,而不對非靜態數據成員進行操作。

綜上:

靜態全局變量

定義:在全局變量前,加上關鍵字 static 該變量就被定義成為了一個靜態全局變量。

特點:
  A、該變量在全局數據區分配內存。
  B、初始化:如果不顯式初始化,那麽將被隱式初始化為0。

靜態局部變量


定義:在局部變量前加上static關鍵字時,就定義了靜態局部變量。


特點:
  A、該變量在全局數據區分配內存。
  B、初始化:如果不顯式初始化,那麽將被隱式初始化為0。
  C、它始終駐留在全局數據區,直到程序運行結束。但其作用域為局部作用域,當定義它的函數或語句塊結束時,其作用域隨之結束。


靜態數據成員

特點:
  A、內存分配:在程序的全局數據區分配。
  B、初始化和定義:
    a、靜態數據成員定義時要分配空間,所以不能在類聲明中定義。
    b、為了避免在多個使用該類的源文件中,對其重復定義,所在,不能在類的頭文件中
    定義。
    c、靜態數據成員因為程序一開始運行就必需存在,所以其初始化的最佳位置在類的內部實現。
  C、特點
    a、對相於 public,protected,private 關鍵字的影響它和普通數據成員一樣,

    b、因為其空間在全局數據區分配,屬於所有本類的對象共享,所以,它不屬於特定的類對象,在沒產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它。
  D、訪問形式
    a、 類對象名.靜態數據成員名

E、靜態數據成員,主要用在類的所有實例都擁有的屬性上。比如,對於一個存款類,帳號相對於每個實例都是不同的,但每個實例的利息是相同的。所以,應該把利息設為存款類的靜態數據成員。這有兩個好處,第一,不管定義多少個存款類對象,利息數據成員都共享分配在全局區的內存,所以節省存貯空間。第二,一旦利息需要改變時,只要改變一次,則所有存款類對象的利息全改變過來了,因為它們實際上是共用一個東西。

靜態成員函數

特點:
  A、靜態成員函數與類相聯系,不與類的對象相聯系。
  B、靜態成員函數不能訪問非靜態數據成員。原因很簡單,非靜態數據成員屬於特定的類實例。
作用:
  主要用於對靜態數據成員的操作。

調用形式:
  類對象名.靜態成員函數名()

一、靜態數據成員

在類中,靜態成員可以實現多個對象之間的數據共享,並且使用靜態數據成員還不會破壞隱藏的原則,保證了安全性。

靜態數據成員在定義或說明時前面加關鍵字static,如:

class A
{
    int n;
    static int s;
};


sizeof 運算符不會計算靜態成員變量,sizeof(CMyclass)等於4。使用靜態數據成員可以節省內存,因為它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共用。
靜態數據成員是靜態存儲的,它是靜態生存期,必須對它進行初始化。靜態成員初始化與一般數據成員初始化不同,類數據成員在類內部聲明,但是靜態成員必須在類的外面初始化,靜態數據成員初始化的格式如下:

<數據類型><類名>::<靜態數據成員名>=<值>

如果一個類中說明了靜態數據成員,只有在這個類的第一個對象被創建時被初始化,自第二個對象起均不作初始化。對A類中靜態數據成員s進行初始化:

    int A::s = 0; 

初始化在類體外進行,而前面不加static,以免與一般靜態變量或對象相混淆。

  static int A::s = 0;  // error C2720: “A::s”: 成員上的“static ”存儲類說明符非法

初始化時不加該成員的訪問權限控制符private,public等。初始化時使用作用域運算符來標明它所屬類,因此,靜態數據成員是類的成員,而不是對象的成員。
引用靜態數據成員時,采用如下格式:
<類名>::<靜態成員名>

類為靜態數據成員只分配了一塊存儲空間。如果一個數據是一個類的所有實例都需要的,而這個數據值的變化對於這個類的所有實例又始終應當是統一的,就應該把這個數據定義為靜態數據成員。

#include <iostream>
using namespace std;
class A
{
public:
    A(int i)
    {
        s += i;
    }
    int n;
    static int s;
};
int A::s = 0;//類外初始化
int main( )
{
    A a1(5);
    A a2(3);
    cout<<"s = "<<A::s<<endl;
    return 0;
}

程序執行結果:

s = 8

靜態數據成員被類的所有對象所共享,包括該類派生類的對象。即派生類對象與基類對象共享基類的靜態數據成員。

#include <iostream>
using namespace std;
class B
{
public :
    static int i;
};
int B::i = 0;
class D:public B
{
};
int main()
{
    B b;
    D d;
    b.i++;
    cout<<"base class static data number i is "<<b.i<<endl;
    d.i++;
    cout<<"derived class static data number i is "<<d.i<<endl;
    return 0;
}

程序執行結果:

base class static data number i is 1
derived class static data number i is 2

二、靜態成員函數


除靜態數據成員以外,一個類還可以有靜態成員函數。靜態函數僅可以訪問靜態成員,或是靜態成員函數或是靜態數據成員。
靜態成員函數和靜態數據成員一樣,它們都屬於類的靜態成員,它們都不是對象成員。因此,對靜態成員的引用不需要用對象名。

class A
{
public:
    void setX(int i)
    {
        x = i;
    };
    static int getN( )
    {
        setX(10);   // error C2352: "A::setX": 非靜態成員函數的非法調用
        y = 100;    //error C2597: 對非靜態成員"A::y"的非法引用
        return s;   //OK
    }
    void func()
    {
        getN();    //OK
    }
private:
    int x;
    int y;
    static int s;
};

因為靜態成員函數屬於整個類,在類實例化之前就已經分配空間了,而類的非靜態成員必須在類的實例化對象之前才能有內存空間,所以類的靜態成員訪問非靜態成員就會出錯,就好像沒有聲明一個變量卻提前使用它一樣。但類的非靜態成員函數卻可以調用靜態成員函數。

class A
{
public:
     static int i;
     static void func( ){}
};
int A::i = 0;
int main( ) 
{
    A c1;
     c1.func( );       // 通過類對象訪問靜態成員函數
     A::func( );       // 通過類名訪問靜態成員函數
     int x = c1.i;      //通過類對象訪問靜態數據成員
     int y = A::i;      //通過類名訪問靜態數據成員
}

從上例從中可看出,調用靜態成員函數使用如下格式:
<類名>::<靜態成員函數名>(<參數表>);
另外,還可通過類的對象來訪問靜態數據成員和靜態成員函數。
在說明前面加了static關鍵字的靜態成員變量為所有對象共享。如果是public的話,那麽靜態成員在沒有對象生成的時候也能直接訪問。靜態成員函數沒有this指針,所以它不需要實例就能運行。

#include <iostream>
using namespace std;
class A
{
public:
    static int i;
    static void func()
    {
        cout<<"i = "<<i<<endl;
    }
};
int A::i = 0;
int main( )
{
    A::func( );       // 通過類名訪問靜態成員函數
    return 0;
}

程序執行結果:

i=0;

同靜態數據成員一樣,靜態成員函數是被類的所有對象所共享,包括該類派生類的對象。

#include <iostream>
using namespace std;
class B
{
public:
    static func()
    {
        i++;
    }
    static int i;
};
int B::i = 0;
class D:public B
{
};
int main()
{
    B b;
    D d;
    b.func();
    cout<<"base class static data number i is "<<b.i<<endl;
    d.func();
    cout<<"derived class static data number i is "<<d.i<<endl;
    return 0;
}

和非靜態成員函數一樣,靜態成員函數可以在派生類中被重定義,派生類會隱藏基類同名的函數。但靜態成員函數不能為virtual函數,這是因為virtual函數由編譯器提供了this指針,而static是沒有this指針的。

三、靜態數據成員和靜態成員函數例子

#include <iostream>
using namespace std;
class Apple
{
private :
    int nWeight;
    static int nTotalWeight;
    static int nTotalNumber;
public:
    Apple( int w)  ;
    ~Apple( ) ;
    static void print( );
};
Apple::Apple( int w)
{
    nWeight = w;
    nTotalWeight += w;
    nTotalNumber ++;
}
Apple::~Apple( )
{
    nTotalWeight -= nWeight;
    nTotalNumber --;
}
void Apple::print()
{
    cout<<"TotalWeight = "<<nTotalWeight<<" TotalNumber = "<<nTotalNumber<<endl;
}
int Apple::nTotalWeight = 0;
int Apple::nTotalNumber = 0;
int main()
{
    Apple a1(6), a2(1);
    Apple::print( );
    return 0;
}

程序執行結果:

TotalWeight = 7 TotalNumber = 2

將上例中的print( )函數改為:

void Apple:: print( )
{  
    cout <<"Weight = "<<nWeight<<" TotalWeight = "<<nTotalWeight<< " TotalNumber = "<< nTotalNumber<<endl;
}


則:
Apple a;
a.print( ); // 解釋得通
Apple::print( ); // 解釋不通,nWeight到底是屬於那個對象的?

上面Apple類的不足之處:在使用Apple類的過程中,有時會調用復制構造函數生成臨時的隱藏的Apple對象(作為參數時,作為返回值時)那麽臨時對象在消亡時會調用析構函數,減少nTotalNumber 和 nTotalWeight的值,可是這些臨時對象在生成時卻沒有增加 nTotalNumber 和 nTotalWeight的值。例如main函數改為:

int main()
{
    Apple a1(6), a2(1);
    {
        Apple a3(a2);
    }
    Apple::print( );
    return 0;
}

此時,程序執行結果為:
TotalWeight = 6 TotalNumber = 1

a3對象是個局部對象,它是通過a2來初始化的,因此會調用復制構造函數,離開作用域時會調用析構函數使TotalWeight和TotalNumber都減少,不該出現的情況發生了:“蘋果被多吃了”。因此,要為Apple類寫一個復制構造函數:

Apple::Apple( Apple & a )
{
        nWeight = a.nWeight;
        nTotalWeight += a.nWeight ;
        nTotalNumber ++;
}







C++ 類的static靜態成員