類和對象

1.類和對象的定義

類是現實世界或思維世界中的實體在計算機中的反映,它將數據以及這些數據上的操作封裝在一起。
對象是具有類類型的變量

2.類和對象的關係

類是對象的抽象,而對象是類的具體實例。類是抽象的,不佔用內存,而對象是具體的,佔用存儲空間。類是用於創建對象的藍圖,它是一個定義包括在特定類型的對象中的方法和變量的軟件模板。

3.類類型的聲明形式

class 類名
{
public:
公用的數據和成員函數
和成員函數
protected:
保護的數據和成員函數
private:
私有的數據
}

4.定義對象的方法

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種方法。在定義一個對象時,編譯系統會爲這個對象分配存儲空間,以存放對象中的成員。

5.類和結構體類型的異同

C++增加了class類型後,仍保留了結構體類型(struct ),而且把它的功能也擴展了。C++允許用struct來定義一個類型。如可以將前面用關鍵字class聲明的類類型改爲用關鍵字struct。
爲了使結構體類型也具有封裝的特徵,C++不是簡單地繼承C的結構體,而是使它也具有類的特點,以便於用於面向對象程序設計
用struct聲明的結構體類型實際上也就是類。用struct聲明的類,如果對其成員不作private或public的聲明,系統將其默認爲public。
如果想分別指定私有成員和公用成員,則應用private或public作顯式聲明。
而用class定義的類,如果不作private或public聲明,系統將其成員默認爲private,在需要時也可以自己用顯式聲明改變。
如果希望成員是公用的,使用struct比較方便,如果希望部分成員是私有的,宜用class。建議儘量使用class來建立類,寫出完全體現C++風格的程序。

編輯本段二、類的成員函數

1.成員函數的性質

類的成員函數(簡稱類函數)是函數的一種,它的用法和作用和第4章介紹過的函數基本上是一樣的,它也有返回值和函數類型,
它與一般函數的區別只是:
它是屬於一個類的成員,出現在類體中。
它可以被指定爲private(私有的)、public (公用的)或protected(受保護的)。
在使用類函數時,要注意調用它的權限(它能否被調用)以及它的作用域(函數能使用什麼範圍中的數據和函數)。
例如私有的成員函數只能被本類中的其它成員函數所調用,而不能被類外調用。
成員函數可以訪問本類中任何成員(包括私有的和公用的),可以引用在本作用域中有效的數據。
一般的做法是將需要被外界調用的成員函數指定爲public,它們是類的對外接口。
但應注意,並非要求把所有成員函數都指定爲public。有的函數並不是準備爲外界調用的,而是爲本類中的成員函數所調用的,就應該將它們指定爲private。
這種函數的作用是支持其它函數的操作,是類中其它成員的工具函數(utility function),類外用戶不能調用這些私有的工具函數。
類的成員函數是類體中十分重要的部分。如果一個類中不包含成員函數,就等同於C語言中的結構體了,體現不出類在面向對象程序設計中的作用。

2.在類外定義成員函數

class Student
{ public :
void display( );
//公用成員函數原型聲明
private :
int num;
string name;
char sex;
//以上3行是私有數據成員
};
void Student∷display( )
//在類外定義display類函數
{cout<<″num:″<<num<<endl;
cout<<″name:″<<name<<endl;
cout<<″sex:″<<sex<<endl;
}
Student stud1,stud2;
//定義兩個類對象
注意:在類體中直接定義函數時,不需要在函數名前面加上類名,因爲函數屬於哪一個類是不言而喻的。
但成員函數在類外定義時,必須在函數名前面加上類名,予以限定(qualifed),“∷”是作用域限定符(field qualifier)或稱作用域運算符,用它聲明函數是屬於哪個類的。
如果在作用域運算符“∷”的前面沒有類名,或者函數名前面既無類名又無作用域運算符“∷”,
如 ∷display( ) 或 display( ),則表示display函數不屬於任何類,這個函數不是成員函數,而是全局函數,即非成員函數的一般普通函數。
類函數必須先在類體中作原型聲明,然後在類外定義,也就是說類體的位置應在函數定義之前,否則編譯時會出錯。
雖然函數在類的外部定義,但在調用成員函數時會根據在類中聲明的函數原型找到函數的定義(函數代碼),從而執行該函數。
在類的內部對成員函數作聲明,而在類體外定義成員函數,這是程序設計的一種良好習慣。
如果一個函數,其函數體只有2-3行,一般可在聲明類時在類體中定義。多於3行的函數,一般在類體內聲明,在類外定義。

3.inline 成員函數

在類體中定義的成員函數的規模一般都很小,而系統調用函數的過程所花費的時間開銷相對是比較大的。調用一個函數的時間開銷遠遠大於小規模函數體中全部語句的執行時間。
爲了減少時間開銷,如果在類體中定義的成員函數中不包括循環等控制結構,C++系統會自動將它們作爲內置(inline )函數來處理。
也就是說,在程序調用這些成員函數時,並不是真正地執行函數的調用過程(如保留返回地址等處理),而是把函數代碼嵌入程序的調用點。
這樣可以大大減少調用成員函數的時間開銷。C++要求對一般的內置函數要用關鍵字inline聲明,但對類內定義的成員函數,可以省略inline,因爲這些成員函數已被隱含地指定爲內置函數。如
class Student
{public :
void display( )
{cout<<″num:″<<num<<endl;cout<<″name:″
<<name<<endl;cout<<″sex:″<<sex<<endl;}
private :
int num;
string name;
char sex;
};
其中第3行
void display( ) 也可以寫成
inline void display( )
將display函數顯式地聲明爲內置函數
以上兩種寫法是等效的。對在類體內定義的函數,一般都省寫inline。
應該注意的是: 如果成員函數不在類體內定義,而在類體外定義,系統並不把它默認爲內置(inline )函數,調用這些成員函數的過程和調用一般函數的過程是相同的。如果想將這些成員函數指定爲內置函數,應當用inline作顯式聲明。如
class Student
{
public : inline void display( );//聲明此成員函數爲內置函數
private :
int num;
string name;
char sex;
};
inline void Student∷display( ) // 在類外定義display函數爲內置函數
{cout<<″num:″<<num<<endl;cout<<″name:″<<name<<endl;cout<<″sex:″<<sex<<endl;}
在函數的聲明或函數的定義兩者之一作inline聲明即可。
值得注意的是: 如果在類體外定義inline函數,則必須將類定義和成員函數的定義都放在同一個頭文件中(或者寫在同一個源文件中),否則編譯時無法進行置換(將函數代碼的拷貝嵌入到函數調用點)。
但是這樣做,不利於類的接口與類的實現分離,不利於信息隱蔽。雖然程序的執行效率提高了,但從軟件工程質量的角度來看,這樣做並不是好的辦法。
只有在類外定義的成員函數規模很小而調用頻率較高時,纔將此成員函數指定爲內置函數

4.成員函數的存儲方式

用類去定義對象時,系統會爲每一個對象分配存儲空間。
如果一個類包括了數據和函數,要分別爲數據和函數的代碼分配存儲空間
按理說,如果用同一個類定義了10個對象,那麼就需要分別爲10個對象的數據和函數代碼分配存儲單元
能否只用一段空間來存放這個共同的函數代碼段,在調用各對象的函數時,都去調用這個公用的函數代碼。  
顯然,這樣做會大大節約存儲空間。C++編譯系統正是這樣做的,因此每個對象所佔用的存儲空間只是該對象的數據部分所佔用的存儲空間,而不包括函數代碼所佔用的存儲空間。如果聲明瞭一個類:
class Time
{
public:
int hour;
int minute;
int sec;
void set( )
{cin>>a>>b>>c;}
};
可以用下面的語句來輸出該類對象所佔用的字節數:
cout<<sizeof(Time)<<endl;
輸出的值是12。
這就證明了一個對象所佔的空間大小隻取決於該對象中數據成員所佔的空間,而與成員函數無關。
函數代碼是存儲在對象空間之外的。如果對同一個類定義了10個對象,這些對象的成員函數對應的是同一個函數代碼段,而不是10個不同的函數代碼段。
需要注意的是: 雖然調用不同對象的成員函數時都是執行同一段函數代碼,但是執行結果一般是不相同的。
不同的對象使用的是同一個函數代碼段,它怎麼能夠分別對不同對象中的數據進行操作呢?
原來C++爲此專門設立了一個名爲this的指針,用來指向不同的對象。需要說明:
(1) 不論成員函數在類內定義還是在類外定義,成員函數的代碼段都用同一種方式存儲。
(2) 不要將成員函數的這種存儲方式和inline(內置)函數的概念混淆。
(3) 應當說明: 常說的“某某對象的成員函數”,是從邏輯的角度而言的,而成員函數的存儲方式,是從物理的角度而言的,二者是不矛盾的。

編輯本段三、對象成員的引用

1.通過對象名和成員運算符訪問對象中的成員

例如在程序中可以寫出以下語句:
stud1.num=1001;//假設num已定義爲公用的整型數據成員
表示將整數1001賦給對象stud1中的數據成員num。
其中“.”是成員運算符,用來對成員進行限定,指明所訪問的是哪一個對象中的成員。
注意不能只寫成員名而忽略對象名。
訪問對象中成員的一般形式爲
對象名.成員名
不僅可以在類外引用對象的公用數據成員,而且還可以調用對象的公用成員函數,但同樣必須指出對象名,如
stud1.display( );//正確,調用對象stud1的公用成員函數
display( );//錯誤,沒有指明是哪一個對象的display函數
由於沒有指明對象名,編譯時把display作爲普通函數處理。
應該注意所訪問的成員是公用的(public )還是私有的(private )。只能訪問public成員,而不能訪問private成員,如果已定義num爲私有數據成員,下面的語句是錯誤的:
stud1.num=10101;//num是私有數據成員,不能被外界引用
在類外只能調用公用的成員函數。在一個類中應當至少有一個公用的成員函數,作爲對外的接口,否則就無法對對象進行任何操作。

2.通過指向對象的指針訪問對象中的成員

class Time
{
public : //數據成員是公用的
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三者等價。

3.通過對象的引用變量來訪問對象中的成員

如果爲一個對象定義了一個引用變量,它們是共佔同一段存儲單元的,實際上它們是同一個對象,只是用不同的名字表示而已。
因此完全可以通過引用變量來訪問對象中的成員。
如果已聲明瞭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>
using namespace std;
class Time //定義Time類
{public : //數據成員爲公用的
int hour;
int minute;
int sec;};
int main( )
{
Time t1;//定義t1爲Time類對象
cin>>t1.hour;//輸入設定的時間
cin>>t1.minute;
cin>>t1.sec;
//輸出時間:
cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;
return 0;
}
運行情況如下: 1232 43↙
12:32:43
注意:
(1) 在引用數據成員hour,minute,sec時不要忘記在前面指定對象名。
(2) 不要錯寫爲類名,
如寫成
Time.hour,Time.minute,Time.sec是不對的。因爲類是一種抽象的數據類型,並不是一個實體,也不佔存儲空間,而對象是實際存在的實體,是佔存儲空間的,其數據成員是有值的,可以被引用的。
(3) 如果刪去主函數的3個輸入語句,即不向這些數據成員賦值,則它們的值是不可預知的。
Time t1;//定義對象t1
Time &t2=t1;//定義Time類引用變量t2,並初始化爲t1
cout<<t2.hour;//輸出對象t1中的成員hour

例2

引用多個對象的成員。
(1) 程序(a)
#include <iostream>
using namespace std;
class Time
{public :
int hour;
int minute;
int sec;
};
int main( )
{Time t1;//定義對象t1
cin>>t1.hour;//向t1的數據成員輸入數據
cin>>t1.minute;
cin>>t1.sec;
cout<<t1.hour<<″:″<<t1.minute<<″:″<<t1.sec<<endl;//輸出t1中數據成員的值
Time t2;//定義對象t2
cin>>t2.hour;//向t2的數據成員輸入數據
cin>>t2.minute;
cin>>t2.sec;
cout<<t2.hour<<″:″<<t2.minute<<″:″<<t2.sec<<endl;//輸出t2中數據成員的值
return 0;
}
運行情況如下:
1032 43↙
10:32:43
22 32 43↙
22:32:43
程序是清晰易懂的,但是在主函數中對不同的對象一一寫出有關操作,會使程序冗長。爲了
解決這個問題,可以使用函數來進行輸入和輸出。見程序(b)。
(2) 程序(b)
#include <iostream>
using namespace std;
class Time
{public :
int hour;
int minute;
int sec;
};
int main( )
{
void set_time(Time&);//函數聲明
void show_time(Time&);//函數聲明
Time t1;//定義t1爲Time類對象
set_time(t1);//調用set_time函數,向t1對象中的數據成員輸入數據
show_time(t1);//調用show_time函數,輸出t1對象中的數據
Time t2;//定義t2爲Time類對象
set_time(t2);//調用set_time函數,向t2對象中的數據成員輸入數據
show_time(t2);//調用show_time函數,輸出t2對象中的數據
return 0;
}
void set_time(Time& t) //定義函數set_time,形參t是引用變量
{
cin>>t.hour;//輸入設定的時間
cin>>t.minute;
cin>>t.sec;
}
void show_time(Time& t) //定義函數show_time,形參t是引用變量
{
cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;//輸出對象中的數據
}
運行情況與程序(a)相同。
(3) 程序(c)
可以對上面的程序作一些修改,數據成員的值不再由鍵盤輸入,而在調用函數時由實參給出,並在函數中使用默認參數。將程序(b)第8行以下部分改爲
int main( )
{
void set_time(Time&,int hour=0,int
minute=0,int sec=0);//函數聲明
void show_time(Time&);//函數聲明
Time t1;
set_time(t1,12,23,34);//通過實參傳遞時、分、秒的值
show_time(t1);
Time t2;
set_time(t2);//使用默認的時、分、秒的值
show_time(t2);
return 0;
}
void set_time(Time& t,int hour,int minute,int sec)
{
t.hour=hour;
t.minute=minute;
t.sec=sec;
}
void show_time(Time& t)
{
cout<<t.hour<<″:″<<t.minute<<″:″<<t.sec<<endl;
}
程序運行時的輸出爲
12:23:34 (t1中的時、分、秒)
0:0:0 (t2中的時、分、秒)
以上兩個程序中定義的類都只有數據成員,沒有成員函數,這顯然沒有體現出使用類的優越性。在下面的例子中,類體中就包含了成員函數。

例3

將例2的程序改用含成員函數的類來處理。
#include <iostream>
using namespace std;
class Time
{public :
void set_time( );//公用成員函數
void show_time( );//公用成員函數
private : //數據成員爲私有
int hour;
int minute;
int sec;
};
int main( )
{
Time t1;//定義對象t1
t1.set_time( );//調用對象t1的成員函數set_time,向t1的數據成員輸入數據
t1.show_time( );//調用對象t1的成員函數show_time,輸出t1的數據成員的值
Time t2;//定義對象t2
t2.set_time( );//調用對象t2的成員函數set_time,向t2的數據成員輸入數據
t2.show_time( );//調用對象t2的成員函數show_time,輸出t2的數據成員的值
return 0;
}
void Time∷set_time( ) //在類外定義set_time函數
{
cin>>hour;
cin>>minute;
cin>>sec;
}
void Time∷show_time( ) //在類外定義show_time函數
{
cout<< hour<<″:″<< minute<<″:″<< sec<< endl;
}
運行情況與例2中的程序(a)相同。
注意:
(1) 在主函數中調用兩個成員函數時,應指明對象名(t1,t2)。表示調用的是哪一個對象的成員函數。
(2) 在類外定義函數時,應指明函數的作用域(如void Time∷set_time( ))。在成員函數引用本對象的數據成員時,只需直接寫數據成員名,這時C++系統會把它默認爲本對象的數據成員。也可以顯式地寫出類名並使用域運算符
(3) 應注意區分什麼場合用域運算符“∷”,什麼場合用成員運算符“.”,不要搞混。

例4

找出一個整型數組中的元素的最大值。這個問題可以不用類的方法來解決,現在用類來處理,讀者可以比較不同方法的特點。
#include <iostream>
using namespace std;
class Array_max //聲明類
{public : //以下3行爲成員函數原型聲明
void set_value( ); //對數組元素設置值
void max_value( ); //找出數組中的最大元素
void show_value( ); //輸出最大值
private :
int array[10]; //整型數組
int max; //max用來存放最大值
};
void Array_max∷set_value( ) //成員函數定義,向數組元素輸入數值
{ int i;
for (i=0;i<10;i++)
cin>> array[i];
}
void Array_max∷max_value( ) //成員函數定義,找數組元素中的最大值
{int i;
max=array[0];
for (i=1;i<10;i++)
if(array[i]> max) max=array[i];
}
void Array_max∷show_value( ) //成員函數定義,輸出最大值
{cout<< ″max=″<< max;}
int main( )
{Array_max arrmax; //定義對象arrmax
arrmax.set_value( ); //調用arrmax的set_value函數,向數組元素輸入數值
arrmax.max_value( ); //調用arrmax的max_value函數,找出數組元素中的最大值
arrmax.show_value( ); //調用arrmax的show_value函數,輸出數組元素中的最大值
return 0;
}
運行結果如下:
12 12 39 -34 17 134 045 -91 76↙ (輸入10個元素的值)
max=134 (輸入10個元素中的最大值)
請注意成員函數定義與調用成員函數的關係,定義成員函數只是設計了一組操作代碼,並未實際執行,只有在被調用時才真正地執行這一組操作。
可以看出: 主函數很簡單,語句很少,只是調用有關對象的成員函數,去完成相應的操作。
在大多數情況下,主函數中甚至不出現控制結構(判斷結構和循環結構),而在成員函數中使用控制結構。
面向對象的程序設計中,最關鍵的工作是類的設計。所有的數據和對數據的操作都體現在類中。
只要把類定義好,編寫程序的工作就顯得很簡單了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章