聲明定義
類對象的定義
類是現實世界或思維世界中的實體在計算機中的反映,它將數據以及這些數據上的操作封裝在一起。
對象是具有類類型的變數。類和對象是面向對象編程技術中的最基本的概念。
類對象的關係
類是對象的抽象,而對象是類的具體實例。類是抽象的,不占用記憶體,而對象是具體的,占用存儲空間。類是用於創建對象的藍圖,它是一個定義包括在特定類型的對象中的方法和變數的軟體模板。
類類型的聲明
class 類名{ public: 公用的數據和成員函式 protected: 保護的數據和成員函式 private: 私有的數據和成員函式}
定義對象方法
1.先聲明類類型,然後再定義對象
舉例:Student stud1,stud2; //Student是已經聲明的類類型
2.在聲明類類型的同時定義對象
class Student//聲明類類型{ public://先聲明公用部分 void display() { cout<<″num:″<<num<<endl; cout<<″name:″<<name<<endl; cout<<″sex:″<<sex<<endl; } private://後聲明私有部分 int num; char name[20]; char sex;}stud1,stud2;//定義了兩個Student類的對象//在定義Student類的同時,定義了兩個Student類的對象。
3.不出現類名,直接定義對象
class//無類名{ private://聲明以下部分為私有的 ┆ public://聲明以下部分為公用的 ┆}stud1,stud2;//定義了兩個無類名的類對象
直接定義對象,在C++中是合法的、允許的,但卻很少用,也不提倡用。在實際的程式開發中,一般都採用上面3種方法中的第1種方法。在小型程式中或所聲明的類只用於本程式時,也可以用第2種方法。在定義一個對象時,
編譯系統會為這個對象分配存儲空間,以存放對象中的成員。
類結構體異同
C++增加了class類型後,仍保留了
結構體類型(struct ),而且把它的功能也擴展了。C++允許用struct來定義一個類型。如可以將前面用關鍵字class聲明的類類型改為用關鍵字struct。
為了使結構體類型也具有封裝的特徵,C++不是簡單地繼承C的結構體,而是使它也具有類的特點,以便於用於
面向對象程式設計。
用struct聲明的結構體類型實際上也就是類。用struct聲明的類,如果對其成員不作private或public的聲明,系統將其默認為public。
如果想分別指定私有成員和公用成員,則套用private或public作顯式聲明。
而用class定義的類,如果不作private或public聲明,系統將其成員默認為private,在需要時也可以自己用顯式聲明改變。
如果希望成員是公用的,使用struct比較方便,如果希望部分成員是私有的,宜用class。建議儘量使用class來建立類,寫出完全體現C++風格的程式。
成員函式
成員函式性質
類的成員函式(簡稱類函式)是函式的一種,它的用法和作用和第4章介紹過的函式基本上是一樣的,它也有返回值和函式類型,
它是屬於一個類的成員,出現在類體中。
它可以被指定為private(私有的)、public (公用的)或protected(受保護的)。
在使用類函式時,要注意調用它的許可權(它能否被調用)以及它的
作用域(函式能使用什麼範圍中的數據和函式)。
例如私有的成員函式只能被本類中的其它成員函式所調用,而不能被類外調用。
成員函式可以訪問本類中任何成員(包括私有的和公用的),可以引用在本作用域中有效的數據。
一般的做法是將需要被外界調用的成員函式指定為public,它們是類的對外接口。
但應注意,並非要求把所有成員函式都指定為public。有的函式並不是準備為外界調用的,而是為本類中的成員函式所調用的,就應該將它們指定為private。
這種函式的作用是支持其它函式的操作,是類中其它成員的工具函式(utility function),類外用戶不能調用這些私有的工具函式。
類外定義函式
classStudent{public:voiddisplay();//公用成員函式原型聲明private:intnum;stringname;charsex;//以上3行是私有數據成員};voidStudent∷display()//在類外定義display類函式{cout<<″num:″<<num<<endl;//函式體cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}Studentstud1,stud2;//定義兩個類對象
注意:在類體中直接定義函式時,不需要在函式名前面加上類名,因為函式屬於哪一個類是不言而喻的。
但成員函式在類外定義時,必須在函式名前面加上類名,予以限定(qualifed),“∷”是
作用域限定符(field qualifier)或稱作用域運算符,用它聲明函式是屬於哪個類的。
如果在作用域運算符“∷”的前面沒有類名,或者函式名前面既無類名又無作用域運算符“∷”,
如 ∷display( ) 或 display( ),則表示display函式不屬於任何類,這個函式不是成員函式,而是
全局函式,即非成員函式的一般普通函式。
類函式必須先在類體中作原型聲明,然後在類外定義,也就是說類體的位置應在函式定義之前,否則編譯時會出錯。
雖然函式在類的外部定義,但在調用成員函式時會根據在類中聲明的函式原型找到函式的定義(函式代碼),從而執行該函式。
在類的內部對成員函式作聲明,而在類體外定義成員函式,這是程式設計的一種良好習慣。
如果一個函式,其
函式體只有2-3行,一般可在聲明類時在類體中定義。多於3行的函式,一般在類體內聲明,在類外定義。
inline 函式
在類體中定義的成員函式的規模一般都很小,而系統調用函式的過程所花費的時間開銷相對是比較大的。調用一個函式的時間開銷遠遠大於小規模
函式體中全部語句的執行時間。
為了減少時間開銷,如果在類體中定義的成員函式中不包括循環等控制結構,C++系統會自動將它們作為內置(inline )函式來處理。
也就是說,在程式調用這些成員函式時,並不是真正地執行函式的調用過程(如保留返回地址等處理),而是把函式代碼嵌入程式的調用點。
這樣可以大大減少調用成員函式的時間開銷。C++要求對一般的
內置函式要用關鍵字inline聲明,但對類內定義的成員函式,可以省略inline,因為這些成員函式已被隱含地指定為內置函式。如
classStudent{public:voiddisplay(){cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}private:intnum;stringname;charsex;};
其中第3行
void display( ) 也可以寫成
inline void display( )
以上兩種寫法是等效的。對在類體內定義的函式,一般都省寫inline。
應該注意的是: 如果成員函式不在類體內定義,而在類體外定義,系統並不把它默認為內置(inline )函式,調用這些成員函式的過程和調用
一般函式的過程是相同的。如果想將這些成員函式指定為內置函式,應當用inline作顯式聲明。如
classStudent{public:inlinevoiddisplay();//聲明此成員函式為內置函式private:intnum;stringname;charsex;};inlinevoidStudent∷display()//在類外定義display函式為內置函式{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}
在函式的聲明或函式的定義兩者之一作inline聲明即可。
值得注意的是: 如果在類體外定義inline函式,則必須將類定義和成員函式的定義都放在同一個頭檔案中(或者寫在同一個
源檔案中),否則編譯時無法進行置換(將函式代碼的拷貝嵌入到
函式調用點)。
但是這樣做,不利於類的接口與類的實現分離,不利於
信息隱蔽。雖然程式的執行效率提高了,但從
軟體工程質量的角度來看,這樣做並不是好的辦法。
只有在類外定義的成員函式規模很小而調用頻率較高時,才將此成員函式指定為
內置函式。
成員函式存儲
用類去定義對象時,系統會為每一個對象分配存儲空間。
如果一個類包括了數據和函式,要分別為數據和函式的代碼分配存儲空間。
按理說,如果用同一個類定義了10個對象,那么就需要分別為10個對象的數據和函式代碼分配
存儲單元。
能否只用一段空間來存放這個共同的函式
代碼段,在調用各對象的函式時,都去調用這個公用的函式代碼。
顯然,這樣做會大大節約存儲空間。C++
編譯系統正是這樣做的,因此每個對象所占用的存儲空間只是該對象的數據部分所占用的存儲空間,而不包括函式代碼所占用的存儲空間。如果聲明了一個類:
classTime{public:inthour;intminute;intsec;voidset(){cin>>a>>b>>c;}};
可以用下面的語句來輸出該類對象所占用的位元組數:
cout<<sizeof(Time)<<endl;
輸出的值是12。
這就證明了一個對象所占的空間大小隻取決於該對象中數據成員所占的空間,而與成員函式無關。
函式代碼是存儲在對象空間之外的。如果對同一個類定義了10個對象,這些對象的成員函式對應的是同一個函式
代碼段,而不是10個不同的函式代碼段。
需要注意的是: 雖然調用不同對象的成員函式時都是執行同一段函式代碼,但是執行結果一般是不相同的。
不同的對象使用的是同一個函式代碼段,它怎么能夠分別對不同對象中的數據進行操作呢?
原來C++為此專門設立了一個名為this的
指針,用來指向不同的對象。需要說明:
(1) 不論成員函式在類內定義還是在類外定義,成員函式的代碼段都用同一種方式存儲。
(2) 不要將成員函式的這種存儲方式和inline(內置)函式的概念混淆。
(3) 應當說明: 常說的“某某對象的成員函式”,是從邏輯的角度而言的,而成員函式的存儲方式,是從物理的角度而言的,二者是不矛盾的。
成員引用
運算訪問成員
例如在程式中可以寫出以下語句:
stud1.num=1001;//假設num已定義為公用的整型數據成員
表示將整數1001賦給對象stud1中的
數據成員num。
其中“.”是成員運算符,用來對成員進行限定,指明所訪問的是哪一個對象中的成員。
注意不能只寫成員名而忽略對象名。
訪問對象中成員的一般形式為
對象名.成員名
不僅可以在類外引用對象的公用數據成員,而且還可以調用對象的公用成員函式,但同樣必須指出對象名,如
stud1.display( );//正確,調用對象stud1的公用成員函式
display( );//錯誤,沒有指明是哪一個對象的display函式
由於沒有指明對象名,編譯時把display作為普通函式處理。
應該注意所訪問的成員是公用的(public )還是私有的(private )。只能訪問public成員,而不能訪問private成員,如果已定義num為私有
數據成員,下面的語句是錯誤的:
stud1.num=10101;//num是私有數據成員,不能被外界引用
在類外只能調用公用的成員函式。在一個類中應當至少有一個公用的成員函式,作為對外的接口,否則就無法對對象進行任何操作。
指向訪問成員
class Time
{
int hour;
int minute;
};Time t,*p;//定義對象t和
指針變數p
p=&t;//使p指向對象t
cout<<p->hour;//輸出p指向的對象中的成員hour
在p指向t的前提下,p->hour,(*p).hour和t.hour三者等價。
對象訪問成員
如果為一個對象定義了一個
引用變數,它們是共占同一段
存儲單元的,實際上它們是同一個對象,只是用不同的名字表示而已。
因此完全可以通過引用變數來訪問對象中的成員。
如果已聲明了Time類,並有以下定義語句:
Time t1; //定義對象t1
Time &t2=t1;//定義Time類引用變數t2,並使之初始化為t1
cout<<t2.hour;//輸出對象t1中的成員hour
由於t2與t1共占同一段存儲單元(即t2是t1的別名),因此t2.hour就是t1.hour。
套用舉例
例1
最簡單的例子。
#include<iostream>usingnamespacestd;classTime//定義Time類{public://數據成員為公用的inthour;intminute;intsec;};intmain(){Timet1;//定義t1為Time類對象cin>>t1.hour;//輸入設定的時間cin>>t1.minute;cin>>t1.sec;//輸出時間:cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;return0;}
運行情況如下: 1232 43↙
12:32:43
注意:
(1) 在引用
數據成員hour,minute,sec時不要忘記在前面指定對象名。
(2) 不要錯寫為類名,
如寫成
Time.hour,Time.minute,Time.sec是不對的。因為類是一種抽象的數據類型,並不是一個實體,也不占存儲空間,而對象是實際存在的實體,是占存儲空間的,其數據成員是有值的,可以被引用的。
(3) 如果刪去
主函式的3個輸入語句,即不向這些數據成員賦值,則它們的值是不可預知的。
例2
引用多個對象的成員。
(1)程式(a)#include<iostream>usingnamespacestd;classTime{public:inthour;intminute;intsec;};intmain(){Timet1;//定義對象t1cin>>t1.hour;//向t1的數據成員輸入數據cin>>t1.minute;cin>>t1.sec;cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;//輸出t1中數據成員的值Timet2;//定義對象t2cin>>t2.hour;//向t2的數據成員輸入數據cin>>t2.minute;cin>>t2.sec;cout<<t2.hour<<″:″<<t2.minute<<″:″<<t2.sec<<endl;//輸出t2中數據成員的值return0;}運行情況如下:103243↙10:32:43223243↙22:32:43程式是清晰易懂的,但是在主函式中對不同的對象一一寫出有關操作,會使程式冗長。為了解決這個問題,可以使用函式來進行輸入和輸出。見程式(b)。(2)程式(b)#include<iostream>usingnamespacestd;classTime{public:inthour;intminute;intsec;};intmain(){voidset_time(Time&);//函式聲明voidshow_time(Time&);//函式聲明Timet1;//定義t1為Time類對象set_time(t1);//調用set_time函式,向t1對象中的數據成員輸入數據show_time(t1);//調用show_time函式,輸出t1對象中的數據Timet2;//定義t2為Time類對象set_time(t2);//調用set_time函式,向t2對象中的數據成員輸入數據show_time(t2);//調用show_time函式,輸出t2對象中的數據return0;}voidset_time(Time&t)//定義函式set_time,形參t是引用變數{cin>>t.hour;//輸入設定的時間cin>>t.minute;cin>>t.sec;}voidshow_time(Time&t)//定義函式show_time,形參t是引用變數{cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;//輸出對象中的數據}
運行情況與程式(a)相同。
(3) 程式(c)
可以對上面的程式作一些修改,
數據成員的值不再由鍵盤輸入,而在調用函式時由
實參給出,並在函式中使用
默認參數。將程式(b)第8行以下部分改為
intmain(){voidset_time(Time&,inthour=0,intminute=0,intsec=0);//函式聲明voidshow_time(Time&);//函式聲明Timet1;set_time(t1,12,23,34);//通過實參傳遞時、分、秒的值show_time(t1);Timet2;set_time(t2);//使用默認的時、分、秒的值show_time(t2);return0;}voidset_time(Time&t,inthour,intminute,intsec){t.hour=hour;t.minute=minute;t.sec=sec;}voidshow_time(Time&t){cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;}
程式運行時的輸出為
12:23:34 (t1中的時、分、秒)
0:0:0 (t2中的時、分、秒)
以上兩個程式中定義的類都只有
數據成員,沒有成員函式,這顯然沒有體現出使用類的優越性。在下面的例子中,類體中就包含了成員函式。
例3
將例2的程式改用含成員函式的類來處理。
#include<iostream>usingnamespacestd;classTime{public:voidset_time();//公用成員函式voidshow_time();//公用成員函式private://數據成員為私有inthour;intminute;intsec;};intmain(){Timet1;//定義對象t1t1.set_time();//調用對象t1的成員函式set_time,向t1的數據成員輸入數據t1.show_time();//調用對象t1的成員函式show_time,輸出t1的數據成員的值Timet2;//定義對象t2t2.set_time();//調用對象t2的成員函式set_time,向t2的數據成員輸入數據t2.show_time();//調用對象t2的成員函式show_time,輸出t2的數據成員的值return0;}voidTime∷set_time()//在類外定義set_time函式{cin>>hour;cin>>minute;cin>>sec;}voidTime∷show_time()//在類外定義show_time函式{cout<<hour<<″:″<<minute<<″:″<<sec<<endl;}
運行情況與例2中的程式(a)相同。
注意:
(1) 在
主函式中調用兩個成員函式時,應指明對象名(t1,t2)。表示調用的是哪一個對象的成員函式。
(2) 在類外定義函式時,應指明函式的
作用域(如void Time∷set_time( ))。在成員函式引用本對象的
數據成員時,只需直接寫數據成員名,這時C++系統會把它默認為本對象的數據成員。也可以顯式地寫出類名並使用域
運算符。
(3) 應注意區分什麼場合用域運算符“∷”,什麼場合用成員運算符“.”,不要搞混。
例4
找出一個
整型數組中的元素的最大值。這個問題可以不用類的方法來解決,用類來處理,讀者可以比較不同方法的特點。
#include<iostream>usingnamespacestd;classArray_max//聲明類{public://以下3行為成員函式原型聲明voidset_value();//對數組元素設定值voidmax_value();//找出數組中的最大元素voidshow_value();//輸出最大值private:intarray[10];//整型數組intmax;//max用來存放最大值};voidArray_max∷set_value()//成員函式定義,向數組元素輸入數值{inti;for(i=0;i<10;i++)cin>>array[i];}voidArray_max∷max_value()//成員函式定義,找數組元素中的最大值{inti;max=array[0];for(i=1;i<10;i++)if(array[i]>max)max=array[i];}voidArray_max∷show_value()//成員函式定義,輸出最大值{cout<<″max=″<<max;}intmain(){Array_maxarrmax;//定義對象arrmaxarrmax.set_value();//調用arrmax的set_value函式,向數組元素輸入數值arrmax.max_value();//調用arrmax的max_value函式,找出數組元素中的最大值arrmax.show_value();//調用arrmax的show_value函式,輸出數組元素中的最大值return0;}
運行結果如下:
12 12 39 -34 17 134 045 -91 76↙ (輸入10個元素的值)
max=134 (輸入10個元素中的最大值)
請注意成員函式定義與調用成員函式的關係,定義成員函式只是設計了一組操作代碼,並未實際執行,只有在被調用時才真正地執行這一組操作。
可以看出:
主函式很簡單,語句很少,只是調用有關對象的成員函式,去完成相應的操作。
在大多數情況下,主函式中甚至不出現控制結構(判斷結構和
循環結構),而在成員函式中使用控制結構。
只要把類定義好,編寫程式的工作就顯得很簡單了。