1. 程式人生 > >關於static成員變數

關於static成員變數

一直以為對static成員變數還算了解。直到昨晚看了《Effective C++》3rd Item49,才發現自己其實什麼都不懂,真是慚愧!所以寫下這篇隨筆,為以後定期回顧參考,也希望大家不要犯類似的錯誤。

先看以下程式碼:

#include <iostream>
using namespace std;

class Base{

             static int itest;

public:

              void Set(){

                               itest++;

              }

               void Show(){

                              cout << itest << endl;

              }

};


int Base::itest = 100;

class Derive: public Base{

};

class Derive2: public Base{

};


int _tmain(int argc, _TCHAR* argv[])
{
   Derive d1;
   Derive2 d2;

   d1.Set();
   d2.Set();

   d1.Show();
   d2.Show();

   return 0;
}
輸出是:

102

102

我以前的理解既然Derive和Derive2是從Base派生的兩個不同的類,那麼他們從Base繼承而來的static變數也應該是不同的兩份拷貝。所以他們應該輸出

101

101

而不是實際上的102.

唉,出現這樣的錯誤看來還是自己的c++基礎太差了。其實對於類的static成員變數它不單獨屬於任何派生類,static成員只存在一份。所有派生類包括基類均共享這唯一的一份。所以才出現了開始的輸出結果。

下面的程式更加證實了我的錯誤:

#include <iostream>
using namespace std;

class Base{

 static int itest;

public:

 void Set(){

  itest++;

 }

 void Show(){

  cout << itest << endl;

 }

};

int Base::itest = 100;

class Derive: public Base{

};

class Derive2: public Base{

};

int _tmain(int argc, _TCHAR* argv[])
{
 Derive d1;
 Derive2 d2;

 cout << sizeof(Base) <<endl;

 cout << &(Base::itest) << endl;

 cout << sizeof( Derive ) << endl;

 cout << &(Derive::itest) << endl;

 cout << sizeof( Derive2) << endl;

 cout << &(Derive2::itest) << endl;

 return 0;

}

程式輸出結果表明Base和Derive以及Derive2的大小都是1. 這個結果表明Base類的static成員變數並未包含在任何一個類空間中。

然而對於定義static變數的類有些時候我們還是希望每個從其派生出來的類都能包含單獨的一份拷貝而不是與基類和其他派生類共享。 能不能實現這樣的想法呢?

答案是肯定的。

先看以下程式碼然後我在做解釋:

#include <iostream>
using namespace std;

template< typename _class >
class Base{

              static int itest;

public:

              void Set(){

                               itest++;

             }

            void Show(){

                              cout << itest << endl;

           }

};

template < typename _class >
int Base< _class >::itest = 100;

class Derive: public Base<Derive>{

};

class Derive2: public Base<Derive2>{

};


int _tmain(int argc, _TCHAR* argv[])
{
         Derive d1;
         Derive2 d2;

         d1.Set();
         d2.Set();

         d1.Show();
         d2.Show();

         return 0;
}

輸出:

101

101

這次的輸出是101,正是我想要的結果。為什麼呢?

原因是這裡用到了模板,我們用不同的引數Base<Derive>、Base<Derive2>去例項化了這個模板,這就意味著Derive和Derive2派生自兩個完全不同的類。因而避開了先前討論的問題。

模板Base中定義的型別引數在Base中並沒有實際的用到,他存在的唯一價值就是為我們提供了標識Base類的機會。也就是我們可以通過指定不同的型別引數而從同一個模板得到完全不同的兩個型別定義。