一、定義 在class的聲明裡頭,真正有用的兩樣東西是data members 和 member functions: Data members:表示根據這個class所產生的object裡頭會有些什麼東西,它事實上也是占據object記憶體的唯一東西(除非引入虛擬機制)。通常為數據的封裝性,我們把d ...
一、定義
在class的聲明裡頭,真正有用的兩樣東西是data members 和 member functions:
Data members:表示根據這個class所產生的object裡頭會有些什麼東西,它事實上也是占據object記憶體的唯一東西(除非引入虛擬機制)。通常為數據的封裝性,我們把data members聲明為private或protected。
Member functions:是用來處理data members的函數。通常為了界面的開放性,我們把member functions設計為pubilc。
二、Data Members(數據成員)
Data members的聲明和一般non-class的變數聲明形式一樣,以CPoint為例:
1 class CPoint 2 { 3 public: 4 CPoint(float x=0.0):_x(x){} 5 6 float x(){return _x;} 7 void x(float xval){_x=xval;} 8 9 protected: 10 float _x; //data member 11 12 };
第10行的_x就是一個data members。
如果data member本身不是一個變數,而是一個class object,這種情況比較特殊,稱為composition(組合),而這種object被稱為embedded objects或object member。Composition被用來描述has a的關係(例如汽車有一個引擎),inheritance(繼承)用來描述is a kind of的關係(例如汽車是一種交通工具)。稍後我們會分別描述composition和inheritance兩種情況。
三、Member Functions(成員函數)
Member functions用來處理class data members。在數據封裝的情況下,member functions可以說就是一個class(或object)的對外界面。這些functions並不占用object的記憶體空間,它被編譯器處理過後,以完全等同一般的global functions的身份出現,獨立於任何object之外。同一份函數代碼如何能夠處理依據同一個class產生出來的不同object的data members呢?關鍵在於有一個this指針隱藏在member functions的參數之中,也隱藏在member functions函數代碼對於data members的處理動作上,是編譯器自動為我們加上去的。
四、如何取用 Class Members
要取得class members,如果是在class scope之內,直接使用其名稱即可。但如果是在class scope之外,你有三種方式可以辦到:
1.透過dot operator(.),例如:
1 CPoint aPoint; // 產生一個 CPoint object,名為 aPoint 2 aPoint.x(); // 喚 起 CPoint::x() 3 aPoint._x = 7.0; // 使 用 CPoint 的 data member
2.透過arrow operator(->),例如:
1 CPoint* pPoint = new CPoint; // 產生一個 CPoint object,由 pPoint 指向它 2 pPoint->x(); // 喚 起 CPoint::x() 3 pPoint->_x = 7.0; // 使用 CPoint 的 data member
3.透過scope resolution operator(::),例如:
1 CPoint::foo(); // foo() must be a static member function 2 CPoint::ratio = 0.2; // ratio must be a static data member
第三種方法未通過任何對象訪問成員,表明這些成員應屬於類層次,即靜態成員(Static Members)。若錯誤地通過對象訪問非靜態成員,如CPoint::foo(),將引發錯誤。靜態成員應通過類名訪問,非靜態成員應通過對象實例訪問。
五、特殊的 Static Members(靜態成員)
Class中可以使用static關鍵,使members成為靜態。
靜態的意思是這份members屬於class而非object所有。聽起來有點奇怪,因為class只是屬性,而object才是實體。很難一言以敝之,讓我們將members細分為data 和function來討論。
當你看完以下的剖析,你會認為static data members和global variables很像,static member functions和global functions 很像。一點沒錯,從功能而言,global 可以取代static。不過,以面對對象的觀點來看,這些members如查在性質上與class息息相關,不是設計成class的static members為佳,面對對象的程式設計中,我們希望global的東西越少越好。
1. Static Data Members(靜態數據成員)
雖說data屬於object實體所有,但程式設計中有些數據也希望由各object共用,所以提升到class層級比較理想。例如我們設計一個銀行儲戶的class:
1 class SavingAccount 2 { 3 private: 4 char m_name[40];//儲戶姓名 5 char m_addr[60];//儲戶地址 6 double m_total;//賬戶金額 7 double m_rate;//利率 8 };
這家銀行採用浮動利率,每個賬戶的利息是根據當天的利率來計算的,這時m_rate就不適合成為每個object的數據了,否則每天一開市,光把所有的賬戶內容調出來,修改m_rate的值,就花了不少時間。m_rate應該獨立於各object之外,成為class的獨一無二的數據;具體怎麼做?在m_rate前面加上Static關鍵字就行了:
1 class SavingAccount 2 { 3 private: 4 char m_name[40];//儲戶姓名 5 char m_addr[60];//儲戶地址 6 double m_total;//賬戶金額 7 Static double m_rate;//利率(註意,這裡只是聲明,不是定義,未分配記憶體) 8 };
Static data members不屬於object的一部份,而是class的一部份,所以程式可以在還沒有生成任何object的時候就處理Static data members。但首先,你必須定義它(為它分配記憶體)。
你應該在.CPP文件中且在class以外的任何區域設定其初始值,例如在main()之中,或在global function中,或任何函數之外:
1 double SavingAccount::m_rate = 0.0075; // 定義並設定初值 2 void main() // 不指定初值也可以 3 { 4 ... 5 }
請註意用詞上的嚴謹:上述“定義”動作常被誤以為是“初始化”動作。如果只是單純的初值設定動作,應該可以安排在class constructor中,但上一行絕對無法如此。此外,你也不應該把上述的定義安排於.H文件中,因為這麼一來它可能會被含入許多.CPP文件中,於是就重覆定義了(會發生連接錯誤)。
上述動作有沒有考慮m_rate是private數據呢?沒有關係,定義static data members,不受任何封裝層級的約束。請註意上述動作也指出static data members的類型,因為這是一個定義動作,不是一個數值指定動作,如果沒有做這個動作,m_rate就沒有獲得記憶體,會發生異常錯誤:
error LNK2001: unresolved external symbol "private: static double SavingAccount::m_rate"(?m_rate@SavingAccount@@2HA)
下麵是存取static data members的一種方式,註意:此時還沒有任何object存在:
1 void main() 2 { 3 SavingAccount::m_rate = 0.0072; // 此行要成立,m_rate 需改為public 4 }
第二種方式是產生一個object後,透過object來處理static data members:
1 void main() 2 { 3 SavingAccount myAccount; 4 myAccount.m_rate = 0.0072; // 此行要成立,m_rate 需改為 public 5 }
請註意,static data members並不是因為myAccount object的實現而才得以實現,它本來就存在。因此第一種的處理方式不容易給人錯誤印象;
2. Static Menber Functions(靜態成員函數)
既然static data members 是超越於object之外存在的,一般的member functions能否處理它呢?要知道,,member function得經由某個object才能調用,該object地址則成為所謂的this指針,但由於static data members並不需要靠this指針的指引來決定其地址,所以其實任何函數(包含global函數),只要access level允許,都可以處理static data members。
但member function得經由某個object才能調用,如果你在尚未產生任何object之前,就希望透過某個member function來更我以為前例的static m_rate,而它又是private(以至於不能被global函數處理)那麼一定要寫個static member function了:
1 class SavingAccount 2 { 3 private: 4 char m_name[40];//儲戶姓名 5 char m_addr[60];//儲戶地址 6 double m_total;//賬戶金額 7 Static double m_rate;//利率(註意,這裡只是聲明,未分配記憶體) 8 public: 9 static void setRate(double newRate){m_rate=newRate;} 10 }; 11 12 double SavingAccount::m_rate=0.0072; //設初值 13 14 int main() 15 { 16 SavingAccount myAccount; 17 //可以透過object調用static member functions 18 myAccount.setRate(0.0072); 19 //也可以直接調用static memberfunction 20 SavingAccount::setRate(0.0072); 21 22 return 0; 23 }
由於static member functions 不需要任何object的存就可以被調用,所以編譯器不會為它暗加一個this指針。也因為如此,static member function無法處理任何non-static data members,因為member function之所以能夠以單一一份函數代碼處理各個object的數據而不亂,完全靠的是this指針。
3. Static Object Members(靜態對象成員)
Object members應該被視為和一般的data members 樣地位,只不過它是class類型,而一般data members是內建類型。當它冠上static,表示它所寄生的class一旦聲明完成(未產生object),並且對static members做了初始化動作,這個static object member就已經在記憶體中有了實體。例如:
1 class Point3d 2 { public: 3 // ... 4 public: // (protected: is better) 5 static Point3d origin; 6 float x, y, z; 7 }; 8 Point3d Point3d::origin; // 別忘了這個動作(其實就是實體配置) 9 10 int main() 11 { 12 printf("&Point3d::x = %p\n", &Point3d::x); // 00000004 13 printf("&Point3d::y = %p\n", &Point3d::y); // 00000008 14 printf("&Point3d::z = %p\n", &Point3d::z); // 0000000C 15 printf("&Point3d::origin = %p\n", &Point3d::origin); // 0040A450 16 printf("&Point3d::origin.x = %p\n", &Point3d::origin.x); // 0040A454 17 printf("&Point3d::origin.y = %p\n", &Point3d::origin.y); // 0040A458 18 printf("&Point3d::origin.z = %p\n", &Point3d::origin.z); // 0040A45C 19 Point3d pt3d; 20 cout << "&pt3d.x = " << &pt3d.x << endl; 21 cout << "&pt3d.y = " << &pt3d.y << endl; 22 // 0x0063FDEC 23 // 0x0063FDF0 24 cout << "&pt3d.z = " << &pt3d.z << endl; // 0x0063FDF4 25 26 }
4. Static Objects(靜態對象)
static 關鍵字也可以應用在object身上,像這樣:
static CPoint aPoint(3.6);
aPoint於是就成為一個static object,這個object的生命將持續到整個程式結束為止。