?? c++10.dat
字號(hào):
第十章 類(lèi)與對(duì)象
第一節(jié) 類(lèi)與對(duì)象的概述
我們的周?chē)且粋€(gè)真實(shí)的世界,不論在何處,我們所見(jiàn)到的東西都可以看成是對(duì)象.人、動(dòng)物、工廠、汽車(chē)、植物、建筑物、割草機(jī)、計(jì)算機(jī)等等都是對(duì)象,現(xiàn)實(shí)世界是由對(duì)象組成的.
對(duì)象多種多樣,各種對(duì)象的屬性也不相同.有的對(duì)象有固定的形狀,有的對(duì)象沒(méi)有固定的形狀,有的對(duì)象有生命,有的對(duì)象沒(méi)有生命,有的對(duì)象可見(jiàn),有的對(duì)象不可見(jiàn),有的對(duì)象會(huì)飛,有的對(duì)象會(huì)跑,有的對(duì)象很高級(jí),而有的對(duì)象很原始,….各個(gè)對(duì)象也有自己的行為,例如:球的滾動(dòng)、彈跳和縮小,嬰兒的啼哭、睡眠、走路和眨眼,汽車(chē)的加速、剎車(chē)和轉(zhuǎn)彎,等等.但是,各個(gè)對(duì)象可能也有一些共同之處,至少它們都是現(xiàn)實(shí)世界的組成部分.
人們是通用過(guò)研究對(duì)象的屬性和觀察它們的行為而認(rèn)識(shí)對(duì)象的.我們可以把對(duì)象分成很多類(lèi),每一大類(lèi)中又可分成若干小類(lèi),也就是說(shuō),類(lèi)是分層的.同一類(lèi)的對(duì)象具有許多相同的屬性和行為,不同類(lèi)的對(duì)象可具有許多相同的屬性和類(lèi)似的行為,例如:嬰兒和成人,人和猩猩,小汽車(chē)和卡車(chē)、四輪馬車(chē)、冰鞋等等都有共同之處,類(lèi)是對(duì)對(duì)象的抽象.
在C++中,就是用類(lèi)來(lái)描述對(duì)象的,類(lèi)是對(duì)現(xiàn)實(shí)世界的抽象得到的.例如,在真實(shí)世界中,同是人類(lèi)的張三和李四,有許多共同點(diǎn),但肯定也有許多不同點(diǎn).當(dāng)用C++描述時(shí),相同類(lèi)的對(duì)象具有相同的屬性和行為,它把對(duì)象分為兩個(gè)部分:數(shù)據(jù)(相當(dāng)于屬性)和對(duì)數(shù)據(jù)的操作(相當(dāng)于行為).我們刻畫(huà)張三和李四的數(shù)據(jù)可能用姓名、性別、年齡、職業(yè)、住址等,而對(duì)數(shù)據(jù)的操作可能是讀或設(shè)置它們他們的名字、年齡等.
從程序設(shè)計(jì)的觀點(diǎn)來(lái)說(shuō),類(lèi)就是數(shù)據(jù)類(lèi)型,是用戶定義的數(shù)據(jù)類(lèi)型.這種類(lèi)型的使用雖然與C++內(nèi)置的數(shù)據(jù)類(lèi)型類(lèi)似,但是也有很大的區(qū)別.例如,C++內(nèi)置的浮點(diǎn)類(lèi)型并不針對(duì)任何具體問(wèn)題,僅僅與機(jī)器的存儲(chǔ)單元相對(duì)應(yīng),而類(lèi)是用戶根據(jù)具體問(wèn)題的需要而定義的,也就是說(shuō),類(lèi)與具體問(wèn)題相適應(yīng).我們可以通過(guò)定義所需要的類(lèi),來(lái)擴(kuò)展程序設(shè)計(jì)語(yǔ)言解決問(wèn)題的能力.
當(dāng)我們把現(xiàn)實(shí)世界分解為一個(gè)個(gè)的對(duì)象,解決現(xiàn)實(shí)世界問(wèn)題的計(jì)算機(jī)程序也與此相對(duì)應(yīng),由一個(gè)個(gè)對(duì)象組成,這些程序就稱為面向?qū)ο蟮某绦?編寫(xiě)面向?qū)ο蟪绦虻倪^(guò)程就稱為面向?qū)ο蟮某绦蛟O(shè)計(jì)(Object-Oriented Programming,簡(jiǎn)稱為OOP).OOP技術(shù)能夠?qū)⒃S多現(xiàn)實(shí)的問(wèn)題歸納成為一個(gè)簡(jiǎn)單解,支持OOP的語(yǔ)言也很多,C++是應(yīng)用最廣泛的、支持OOP的語(yǔ)言,第一個(gè)成功的支持OOP的語(yǔ)言是Smalltalk.
面向?qū)ο蟮某绦蛟O(shè)計(jì)(OOP)使用軟件的方法模擬真實(shí)世界的對(duì)象,它利用了類(lèi)的關(guān)系,即同一對(duì)象(如同一類(lèi)運(yùn)載工具)具有相同的特點(diǎn);還利用了繼承甚至多重繼承的關(guān)系,即新建的對(duì)象類(lèi)是通過(guò)繼承現(xiàn)有類(lèi)的特點(diǎn)而派生出來(lái)的,但是又包含了其自身特有的特點(diǎn),如子女有父母的許多特點(diǎn),但是矮個(gè)子父母的子女也可能是高個(gè)子.
面向?qū)ο蟮某绦蛟O(shè)計(jì)(OOP)使程序設(shè)計(jì)過(guò)程更自然和直觀.也就是說(shuō),面向?qū)ο蟮某绦蛟O(shè)計(jì)模擬了真實(shí)世界的對(duì)象(它們的屬性和行為).OOP還模擬了對(duì)象之間的通信,就像人們之間互送消息一樣(如軍官命令部隊(duì)立正),對(duì)象也是通過(guò)消息進(jìn)行通信的.
C++語(yǔ)言是當(dāng)今應(yīng)用最廣泛的程序設(shè)計(jì)語(yǔ)言,它與C語(yǔ)言兼容,既支持面向?qū)ο蟮某绦蛟O(shè)計(jì),也支持面向?qū)ο蟮某绦蛟O(shè)計(jì)方法.在前面的章節(jié)中,我們編寫(xiě)的程序是由一個(gè)個(gè)函數(shù)組成的,可以說(shuō)是結(jié)構(gòu)化的程序.從本章開(kāi)始,我們編寫(xiě)的程序是由對(duì)象組成的,也就是說(shuō),將要學(xué)習(xí)用C++語(yǔ)言進(jìn)行面向?qū)ο蟮某绦蛟O(shè)計(jì).
什么叫類(lèi),什么叫對(duì)象?我們已經(jīng)知道什么叫變量.假定我們?cè)趍ain函數(shù)中定義了一個(gè)整型變量nInteger:
void main()
{
int nInteger;
…
}
則在main函數(shù)中為nInteger分配棧內(nèi)存,保存變量nInteger的值,并在main返回時(shí),釋放該內(nèi)存.在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,nInteger也稱之為對(duì)象.所謂對(duì)象就是一個(gè)內(nèi)存區(qū),它存儲(chǔ)某種類(lèi)型的數(shù)值,變量就是有名的對(duì)象.對(duì)象除可以用上述定義的方法來(lái)創(chuàng)建外,也可以用new表達(dá)式創(chuàng)建,也可能是應(yīng)用程序運(yùn)行時(shí)臨時(shí)創(chuàng)建的,例如,在函數(shù)調(diào)用和返回時(shí),均會(huì)創(chuàng)建臨時(shí)對(duì)象.
對(duì)象是有類(lèi)型的,例如,我們上面定義的nInteger對(duì)象就是整型的.一個(gè)類(lèi)型可以定義許多對(duì)象,一個(gè)對(duì)象有一個(gè)確定的類(lèi)型,可以這么說(shuō):int型變量是int類(lèi)型的實(shí)例.以后,我們也常說(shuō):對(duì)象是類(lèi)的實(shí)例,那么int是不是一個(gè)類(lèi)呢?
實(shí)際上,我們所說(shuō)的類(lèi),并非指C++中的那些基本的數(shù)據(jù)類(lèi)型.C++中引入了class關(guān)鍵字來(lái)定義類(lèi),它也是一種數(shù)據(jù)類(lèi)型.類(lèi)是C++支持面向?qū)ο蟮某绦蛟O(shè)計(jì)的基礎(chǔ),它支持?jǐn)?shù)據(jù)的封裝、隱藏等.類(lèi)與我們前面學(xué)習(xí)過(guò)的結(jié)構(gòu)類(lèi)似,實(shí)際上C++中也可以用struct關(guān)鍵字來(lái)定義類(lèi)(雖然很少使用).
我們前面學(xué)習(xí)的結(jié)構(gòu)中,只有數(shù)據(jù)成員.實(shí)際上,類(lèi)中除可以定義數(shù)據(jù)成員外,還可以定義對(duì)這些數(shù)據(jù)成員(或?qū)ο?操作的函數(shù),也正是這些函數(shù)限制了對(duì)對(duì)象的操作,即不能對(duì)對(duì)象進(jìn)行這些操作函數(shù)之外的其它操作,類(lèi)的成員也有不同的訪問(wèn)權(quán)限.下面,我們將要介紹怎樣定義類(lèi)及類(lèi)的成員.
1.1 類(lèi)的定義
類(lèi)定義的一般形式如下:
class Name
{
細(xì)節(jié)
};
類(lèi)的定義由頭和體兩個(gè)部分組成.類(lèi)頭由關(guān)鍵字class開(kāi)頭,然后是類(lèi)名,其命名規(guī)則與一般標(biāo)識(shí)符的命名規(guī)則一致,有時(shí)可能有附加的命名規(guī)則,例如美國(guó)微軟公司的MFC類(lèi)庫(kù)中的所有類(lèi)均是以大寫(xiě)字母'C'開(kāi)頭的.類(lèi)體包括所有的細(xì)節(jié),并放在一對(duì)花括號(hào)中.類(lèi)的定義也是一個(gè)語(yǔ)句,所以要有分號(hào)結(jié)尾,否則,會(huì)產(chǎn)生難以理解的編譯錯(cuò)誤.
類(lèi)體定義類(lèi)的成員,它支持兩種類(lèi)型的成員:
1. 數(shù)據(jù)成員,它們指定了該類(lèi)對(duì)象的內(nèi)部表示.
2. 成員函數(shù),它們指定該類(lèi)的操作.
類(lèi)成員有三種不同的訪問(wèn)權(quán)限:
1. 公有(public)成員可以在類(lèi)外訪問(wèn).
2. 私有(private)成員只能被該類(lèi)的成員函數(shù)訪問(wèn).
3. 保護(hù)(protected)成員只能被該類(lèi)的成員函數(shù)或派生類(lèi)(有關(guān)基類(lèi)和派生類(lèi)的概念我們?cè)谙乱徽陆榻B)的成員函數(shù)訪問(wèn).
數(shù)據(jù)成員通常是私有的,成員函數(shù)通常有一部分是公有的,一部分是私有的.公有的成員函數(shù)可在類(lèi)外被訪問(wèn),也稱之為類(lèi)的接口.我們可以為各個(gè)數(shù)據(jù)成員和成員函數(shù)指定合適的訪問(wèn)權(quán)限,類(lèi)定義常有下面的形式:
class Name {
public:
類(lèi)的公有接口
private:
私有的成員函數(shù)
私有的數(shù)據(jù)成員定義
};
私有的成員與公有的成員的先后次序無(wú)關(guān)緊要.不過(guò)公有的接口函數(shù)放在前面更好,因?yàn)?有時(shí)我們可能只想知道怎樣使用一個(gè)類(lèi)的對(duì)象,那只要知道類(lèi)的公有接口就行了,不必閱讀private關(guān)鍵字以下的部分.
1.2 類(lèi)成員函數(shù)的定義
類(lèi)的成員函數(shù)通常在類(lèi)外定義,一般形式如下:
返回類(lèi)型 類(lèi)名::函數(shù)名(形參表)
{
函數(shù)體
}
雙冒號(hào)::是域運(yùn)算符,它主要用于類(lèi)的成員函數(shù)的定義.
1.3 使用對(duì)象
定義了類(lèi)以后,就可以定義類(lèi)類(lèi)型的變量(或?qū)ο?,例如:
void DrawLine(Point& p1, Point& p2)
{
Point MidPoint;
…
}
函數(shù)DrawLine中定義了一個(gè)Point對(duì)象MidPoint.
象結(jié)構(gòu)一樣,類(lèi)類(lèi)型的變量也能夠作為函數(shù)參數(shù),以值或引用的方式傳遞,也可以作為函數(shù)的返回值,以及在賦值語(yǔ)句中被復(fù)制.
我們已經(jīng)知道:結(jié)構(gòu)變量通過(guò)"."運(yùn)算符訪問(wèn)其數(shù)據(jù)成員,對(duì)象數(shù)據(jù)要通過(guò)其成員函數(shù)進(jìn)行修改或讀出.
Point thePoint;
…
thePoint.SetPt(5, 10);
…
if(thePoint.GetX() < 0) …
可以看到,調(diào)用成員函數(shù)語(yǔ)法與結(jié)構(gòu)變量訪問(wèn)其數(shù)據(jù)成員的語(yǔ)法相同:
對(duì)象名. 成員函數(shù)名 (實(shí)參表)
成員函數(shù)也可以通過(guò)指向?qū)ο蟮闹羔?調(diào)用形式為:
指向?qū)ο蟮闹羔?>成員函數(shù)名 (實(shí)參表)
注意:
(1) 對(duì)私有數(shù)據(jù)成員的訪問(wèn)只能通過(guò)成員函數(shù),下面的語(yǔ)句是非法的:
thePoint.xVal = 5; // 非法
(2) 不要混淆了類(lèi)與對(duì)象的概念.類(lèi)是用戶定義的數(shù)據(jù)類(lèi)型(不占內(nèi)存),對(duì)象是類(lèi)的實(shí)例(占內(nèi)存單元),例如:
Point pt1, pt2, pt3;
定義了三個(gè)Point對(duì)象pt1、pt2和pt3.
例如:
動(dòng)態(tài)對(duì)象創(chuàng)建
有時(shí)我們知道程序中需要?jiǎng)?chuàng)建多少對(duì)象,但是多數(shù)情況下,我們不能預(yù)知所需對(duì)象的確切數(shù)量.比如公路交通管理系統(tǒng)必須同時(shí)處理多少輛汽車(chē)?一個(gè)三維建筑設(shè)計(jì)系統(tǒng)需要處理多少個(gè)模型?解決這些編程問(wèn)題的方法,是要能夠支持在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建和銷(xiāo)毀對(duì)象.
一個(gè)對(duì)象被動(dòng)態(tài)創(chuàng)建時(shí),依次發(fā)生兩件事情:
1. 為對(duì)象分配內(nèi)存;
2. 調(diào)用構(gòu)造函數(shù)來(lái)初始化這塊內(nèi)存.
同樣,一個(gè)對(duì)象被動(dòng)態(tài)銷(xiāo)毀時(shí),按照順序發(fā)生了下面兩件事情:
1. 調(diào)用析構(gòu)函數(shù)清除對(duì)象;
2. 釋放對(duì)象的內(nèi)存;
C++提供了兩個(gè)運(yùn)算符new和delete,分別用來(lái)完成動(dòng)態(tài)對(duì)象的創(chuàng)建和銷(xiāo)毀.當(dāng)我們用new創(chuàng)建一個(gè)對(duì)象時(shí),就在堆里為對(duì)象分配內(nèi)存并調(diào)用相應(yīng)的構(gòu)造函數(shù).new返回一個(gè)指向剛剛創(chuàng)建的對(duì)象的指針;當(dāng)我們用delete銷(xiāo)毀一個(gè)對(duì)象時(shí),就調(diào)用相應(yīng)的析構(gòu)函數(shù),釋放掉分配的堆內(nèi)存,delete運(yùn)算符的操作數(shù)是指向?qū)ο蟮闹羔?需要注意的是:用new創(chuàng)建的對(duì)象必須用delete銷(xiāo)毀,否則,會(huì)出現(xiàn)內(nèi)存泄漏.
舉個(gè)例子說(shuō)明利用new和delete動(dòng)態(tài)創(chuàng)建和銷(xiāo)毀對(duì)象的過(guò)程:
#include <iostream.h>
class Tree
{
public:
Tree(int height)
{
cout<<"tree object is creating"<<endl;
this->height = height;
}
~Tree()
{
cout<<"tree object is deleting"<<endl;
}
void display()
{
cout<<"this tree is "<<height<<" meters high"<<endl;
}
private:
int height;
};
void main()
{
Tree* tree = new Tree (100);
tree->display();
delete tree;
}
程序的輸出結(jié)果如下:
tree object is creating
this tree is 100 meters high
tree object is deleting
main( )函數(shù)中的第一個(gè)語(yǔ)句,是用new運(yùn)算符動(dòng)態(tài)創(chuàng)建一個(gè)Tree類(lèi)對(duì)象,new后面括號(hào)中的100,實(shí)際上是new創(chuàng)建對(duì)象時(shí),傳給構(gòu)造函數(shù)的參數(shù).main( )函數(shù)的第二個(gè)語(yǔ)句是調(diào)用對(duì)象的顯示函數(shù),打印出的結(jié)果顯示樹(shù)高為100米,可見(jiàn)new操作符確實(shí)調(diào)用了類(lèi)的構(gòu)造函數(shù)display.main( )函數(shù)的最后一個(gè)語(yǔ)句用delete運(yùn)算符銷(xiāo)毀用new創(chuàng)建的對(duì)象.對(duì)象一旦被銷(xiāo)毀后,就不再存在.如果繼續(xù)訪問(wèn)對(duì)象tree的數(shù)據(jù)成員或成員函數(shù),則程序會(huì)產(chǎn)生錯(cuò)誤.
1.4 this指針
C++中,定義了一個(gè)this指針,用它指向調(diào)用非靜態(tài)成員函數(shù)的對(duì)象.也就是說(shuō),this指針僅能在類(lèi)的成員函數(shù)中訪問(wèn),它指向調(diào)用該函數(shù)的對(duì)象,在后面我們要介紹的靜態(tài)成員函數(shù)沒(méi)有this指針.
當(dāng)一個(gè)非靜態(tài)的成員函數(shù)被一個(gè)對(duì)象調(diào)用時(shí),對(duì)象的地址作為一個(gè)隱含的參數(shù)傳給被調(diào)用的函數(shù).例如:
myDate.setMonth( 3 );
可被解釋為:
setMonth( &myDate, 3 );
下面的成員函數(shù)setMonth,可用兩種方法實(shí)現(xiàn):
void Date::setMonth( int mn )//使用隱含的this指針
{
month = mn;
}
void Date::setMonth( int mn )//顯式使用this指針
{
this->month = mn;
}
雖然顯式使用this指針的情況并不是很多,但是,this指針有時(shí)是有用的.例如,下面的賦值是不允許的:
void Date::setMonth( int month)
{
month = month;
}
但可以用this指針來(lái)解決:
void Date::setMonth( int month)
{
this->month = month;
}
類(lèi)的每個(gè)成員函數(shù)都訪問(wèn)特殊的指針-this.This指針保留了激活成員函數(shù)的對(duì)象地址(也就是說(shuō),this總是指向目標(biāo)對(duì)象).This指針僅僅在成員函數(shù)內(nèi)部是合法的,而且名稱this是C++的保留字.
每個(gè)成員函數(shù)所收到的第一個(gè)參數(shù)就是this指針.程序員沒(méi)有必要明確定義這個(gè)this指針,但是,它總是存在的.This指針通常是每個(gè)成員函數(shù)(非靜態(tài))隱含的第一個(gè)參數(shù).編譯器在每個(gè)成員函數(shù)的聲明中插入這個(gè)隱含參數(shù).當(dāng)成員函數(shù)使用類(lèi)的成員的絕對(duì)名稱的任何時(shí)候,它隱式使用this指針.編譯器在引用成員函數(shù)內(nèi)部的每個(gè)表達(dá)式中插入this指針(如果用戶并沒(méi)有這樣做的話).
第二節(jié) 構(gòu)造函數(shù)與析構(gòu)函數(shù)
在C++中,有兩種特殊的成員函數(shù),即是構(gòu)造函數(shù)和析構(gòu)函數(shù),下面分別予以介紹.
2.1 構(gòu)造函數(shù)
變量應(yīng)該被初始化,我們已經(jīng)知道了簡(jiǎn)單變量的初始化、數(shù)組的初始化、結(jié)構(gòu)和結(jié)構(gòu)數(shù)組的初始化.對(duì)象也需要初始化,應(yīng)該怎樣初始化一個(gè)對(duì)象呢?
C++中定義了一種特殊的初始化函數(shù),稱之為構(gòu)造函數(shù).當(dāng)對(duì)象被創(chuàng)建時(shí),構(gòu)造函數(shù)自動(dòng)被調(diào)用.構(gòu)造函數(shù)有一些獨(dú)特的地方:函數(shù)的名字與類(lèi)名相同,它也沒(méi)有返回類(lèi)型和返回值.
例如:
class Point {
public:
Point(int x = 0, int y = 0);
…
};
類(lèi)Point的構(gòu)造函數(shù)有兩個(gè)參數(shù),它們是賦給xVal、yVal的初始值,該構(gòu)造函數(shù)的原型也為實(shí)參指定了缺省值0.
Point::Point(double x, double y)
{
xVal = x;
yVal = y;
}
我們也可以把該構(gòu)造函數(shù)定義為內(nèi)聯(lián)函數(shù)的形式:
class Point {
Point (int x=0, int y=0) {xVal = x; yVal = y;} // 構(gòu)造函數(shù)
…
};
如果不采用Point構(gòu)造函數(shù),我們將不得不調(diào)用SetPt函數(shù)初始化.可見(jiàn),使用Point構(gòu)造函數(shù)方便了編程,簡(jiǎn)化了程序代碼.
現(xiàn)在,我們可以定義Point類(lèi)對(duì)象并立即初始化它們:
Point pt1(10, 20); // xVal和yVal的初值分別為10和20
Point pt2; // xVal和yVal的初值均為0
構(gòu)造函數(shù)也可以重載:
class Point {
int xVal, yVal;
public:
Point (int x, int y) { xVal = x; yVal = y; }
Point (float, float); //極坐標(biāo)
Point (void) { xVal = yVal = 0; }
…
};
Point::Point (float len, float angle) //極坐標(biāo)
{
xVal = (int) (len * cos(angle));
yVal = (int) (len * sin(angle));
}
創(chuàng)建Point對(duì)象時(shí),可以使用這三種構(gòu)造函數(shù)中的任一個(gè).
Point pt1(10, 20); // 笛卡兒坐標(biāo)
Point pt2(60.3, 3.14); // 極坐標(biāo)
Point pt3; // 原點(diǎn)
2.2 成員初始化表
一、常量成員
二、引用成員
三、類(lèi)對(duì)象成員
類(lèi)中數(shù)據(jù)成員可以通過(guò)構(gòu)造函數(shù)來(lái)初始化,例如:
程序1
class Image {
public:
Image(const int w, const int h);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -