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