C++對象指標
指向對象的指標
在建立對象時,編譯系統會為每一個對象分配一定的儲存空間,以存放其成員。對象空間的起始地址就是對象的指標。可以定義一個指標變數,用來存放對象的指標。
如果有一個類:
class Time{ public : int hour; int minute; int sec; void get_time( );};void Time::get_time( ){ cout<<hour<<":"<<minute<<":"<<sec<<endl;}
在此基礎上有以下語句:
Time *pt; //定義pt為指向Time類對象的指標變數 Time t1; //定義t1為Time類對象 pt=&t1; //將t1的起始地址賦給pt
這樣,pt就是指向Time類對象的指標變數,它指向對象t1。
定義指向類對象的指標變數的一般形式為:
可以通過對象指標訪問對象和對象的成員。如:
*pt //pt所指向的對象,即t1 (*pt).hour //pt所指向的對象中的hour成員,即t1.hour pt->hour //pt所指向的對象中的hour成員,即t1.hour (*pt).get_time ( ) //調用pt所指向的對象中的get_time函數,即t1.get_time pt->get_time ( ) //調用pt所指向的對象中的get_time函數,即t1.get_time
上面第2, 3行的作用是等價的,第4, 5兩行也是等價的。
指向對象成員的指標
對象有地址,存放對象初始地址的指標變數就是指向對象的指標變數。對象中的成員也有地址,存放對象成員地址的指標變數就是指向對象成員的指標變數。
1) 指向對象資料成員的指標
定義指向對象資料成員的指標變數的方法和定義指向普通變數的指標變數方法相同。例如:
定義指向對象資料成員的指標變數的一般形式為:
如果Time類的資料成員hour為公用的整型資料,則可以在類外通過指向對象資料成員的指標變數訪問對象資料成員hour:
p1=&t1.hour; //將對象t1的資料成員hour的地址賦給p1,p1指向t1.hour cout<<*p1<<endl; //輸出t1.hour的值
2) 指向對象成員函數的指標
需要提醒讀者注意: 定義指向對象成員函數的指標變數的方法和定義指向普通函數的指標變數方法有所不同。這裡重溫一個指向普通函數的指標變數的定義方法:
資料類型名 (*指標變數名) (參數表列);
如
void ( *p)( ); //p是指向void型函數的指標變數
可以使它指向一個函數,並通過指標變數調用函數:
p = fun; //將fun函數的人口地址傳給指標變童p,p就指向了函數fn
(*P)( ); //調用fn函數
而定義一個指向對象成員函數的指標變數則比較複雜一些。如果模仿上面的方法將對象成員函數名賦給指標變最P:
則會出現編譯錯誤。為什麼呢?
成員函數與普通函數有一個最根本的區別: 它是類中的一個成員。編譯系統要求在上面的指派陳述式中,指標變數的類型必須與賦值號右側函數的類型相匹配,要求在以下3方面都要匹配:
①函數參數的類型和參數個數;
②函數傳回值的類型;
③所屬的類。
現在3點中第①②兩點是匹配的,而第③點不匹配。指標變數p與類無關,面get_ time函數卻屬於Time類。因此,要區別普通函數和成員函數的不同性質,不能在類外直接用成員函數名作為函數入口地址去調用成員函數。
那麼,應該怎樣定義指向成員函數的指標變數呢?應該採用下面的形式:
void (Time::*p2)( ); //定義p2為指向Time類中公用成員函數的指標變數
注意:(Time:: *p2) 兩側的括弧不能省略,因為()的優先順序高於*。如果無此括弧,就相當於:
void Time::*(p2()) //這是傳回值為void型指標的函數
定義指向公用成員函數的指標變數的一般形式為:
資料類型名 (類名::*指標變數名)(參數表列);
可以讓它指向一個公用成員函數,只需把公用成員函數的入口地址賦給一個指向公用成員函數的指標變數即可。如:
使指標變數指向一個公用成員函數的一般形式為
在VC++系統中,也可以不寫&,以和C語言的用法一致,但建議在寫C++程式時不要省略&。
[例]有關對象指標的使用方法。
#include <iostream>using namespace std;class Time{ public: Time(int,int,int); int hour; int minute; int sec; void get_time( );};Time::Time(int h,int m,int s){ hour=h; minute=m; sec=s;}void Time::get_time( ) //聲明公有成員函數//定義公有成員函數{ cout<<hour<<":"<<minute<<":" <<sec<<endl;}int main( ){ Time t1(10,13,56); //定義Time類對象t1 int *p1=&t1.hour; //定義指向整型資料的指標變數p1,並使p1指向t1.hour cout<<* p1<<endl; //輸出p1所指的資料成員t1.hour t1.get_time( ); //調用對象t1的成員函數get_time Time *p2=&t1; //定義指向Time類對象的指標變數p2,並使p2指向t1 p2->get_time( ); //調用p2所指向對象(即t1)的get_time函數 void (Time::*p3)( ); //定義指向Time類公用成員函數的指標變數p3 p3=&Time::get_time; //使p3指向Time類公用成員函數get_time (t1.*p3)( ); //調用對象t1中p3所指的成員函數(即t1.get_time( )) return 0;}
程式運行結果為:
10 (main函數第4行的輸出)10:13:56 (main函數第5行的輸出)10:13:56 (main函數第7行的輸出)10:13:56 (main函數第10行的輸出)
可以看到為了輸出t1中hour,minute和sec的值,可以採用3種不同的方法。
幾點說明:
1) 從main函數第9行可以看出,成員函數的入口地址的正確寫法是:
不應該寫成:
p3 =&t1.get_time; //t1為對象名
成員函數不是存放在對象的空間中的,而是存放在對象外的空間中的。如果有多個同類的對象,它們共用同一個函數程式碼片段。因此賦給指標變數p3的應是這個公用的函數程式碼片段的入口地址。
調用t1的get_time函數可以用t1.get_time()形式,那是從邏輯的角度而言的,通過對象名能調用成員函數。而現在程式語句中需要的是地址,它是物理的,具體地址是和類而不是對象相聯絡的。
2) main函數第8, 9兩行可以合寫為一行:
void (Time::*p3)( )=&Time::get_time; //定義指標變數時指定其指向
C++對象數組
數組不僅可以由簡單變數組成(例如整型數組的每一個元素都是整型變數),也可以由對象組成(對象數組的每一個元素都是同類的對象)。
在日常生活中,有許多實體的屬性是共同的,只是屬性的具體內容不同。例如一個班有50個學生,每個學生的屬性包括姓名、性別、年齡、成績等。如果為每一個學生建立一個對象,需要分別取50個對象名。用程式處理很不方便。這時可以定義一個“學生類”對象數組,每一個數組元素是一個“學生類”對象。例如
Student stud[50]; //假設已聲明了Student類,定義stud數組,有50個元素
在建立數組時,同樣要調用建構函式。如果有50個元素,需要調用50次建構函式。
在需要時可以在定義數組時提供實參以實現初始化。如果建構函式只有一個參數,在定義數組時可以直接在等號後面的花括弧內提供實參。如
Student stud[3]={60,70,78}; //合法,3個實參分別傳遞給3個數組元素的建構函式
如果建構函式有多個參數,則不能用在定義數組時直接提供所有實參的方法,因為一個數組有多個元素,對每個元素要提供多個實參,如果再考慮到建構函式有預設參數的情況,很容易造成實參與形參的對應關係不清晰,出現歧義性。例如,類Student的建構函式有多個參數,且為預設參數:
Student:: Student(int=1001,int=18,int=60); //定義建構函式,有多個參數,且為預設參數
如果定義對象數組的語句為
Student stud[3]={1005,60,70};
在程式中最好不要採用這種容易引起歧義性的方法。
編譯系統只為每個對象元素的建構函式傳遞一個實參,所以在定義數組時提供的實參個數不能超過數組元素個數,如
Student stud[3]={60,70,78,45}; //不合法,實參個數超過對象數組元素個數
那麼,如果建構函式有多個參數,在定義對象數組時應當怎樣實現初始化呢?回答是: 在花括弧中分別寫出建構函式並指定實參。
如果建構函式有3個參數,分別代表學號、年齡、成績。則可以這樣定義對象數組:
Student Stud[3]={ //定義對象數組 Student(1001,18,87), //調用第1個元素的建構函式,為它提供3個實參 Student(1002,19,76), //調用第2個元素的建構函式,為它提供3個實參 Student(1003,18,72) //調用第3個元素的建構函式,為它提供3個實參};
在建立對象數組時,分別調用建構函式,對每個元素初始化。每一個元素的實參分別用括弧包起來,對應建構函式的一組形參,不會混淆。
[例] 對象數組的使用方法。
#include <iostream>using namespace std;class Box{public : //聲明有預設參數的建構函式,用參數初始化表對資料成員初始化 Box(int h=10,int w=12,int len=15): height(h),width(w),length(len){ } int volume( );private : int height; int width; int length;};int Box::volume( ){ return (height*width*length);}int main( ){ Box a[3]={ //定義對象數組 Box(10,12,15), //調用建構函式Box,提供第1個元素的實參 Box(15,18,20), //調用建構函式Box,提供第2個元素的實參 Box(16,20,26) //調用建構函式Box,提供第3個元素的實參 }; cout<<"volume of a[0] is "<<a[0].volume( )<<endl; cout<<"volume of a[1] is "<<a[1].volume( )<<endl; cout<<"volume of a[2] is "<<a[2].volume( )<<endl; return 0;}
運行結果如下:
volume of a[0] is 1800volume of a[1] is 5400volume of a[2] is 8320