程序設(shè)計基礎(chǔ)-第6章-數(shù)據(jù)抽象類_第1頁
程序設(shè)計基礎(chǔ)-第6章-數(shù)據(jù)抽象類_第2頁
程序設(shè)計基礎(chǔ)-第6章-數(shù)據(jù)抽象類_第3頁
程序設(shè)計基礎(chǔ)-第6章-數(shù)據(jù)抽象類_第4頁
程序設(shè)計基礎(chǔ)-第6章-數(shù)據(jù)抽象類_第5頁
已閱讀5頁,還剩103頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

描述三維幾何模型

數(shù)據(jù):

點、線、面

幾何屬性(法向、曲率)

拓?fù)潢P(guān)系

操作:、保存數(shù)據(jù)

幾何操作

打光照

貼紋理

…描述學(xué)生

數(shù)據(jù):、

、

、籍貫、成績、

操作:

保存、導(dǎo)出數(shù)據(jù)

計算平均成績

…第六章

數(shù)據(jù)抽象------類與對象2012年12月Ps.

改編自陳家俊老師相關(guān)課件本章內(nèi)容

數(shù)據(jù)抽象與封裝

類和對象

對象的初始化和消亡前處理

常(const)成員

靜態(tài)(static)成員(friend)數(shù)據(jù)抽象與封裝

數(shù)據(jù)抽象

數(shù)據(jù)的使用者只需要知道對數(shù)據(jù)所能實施的操作以及這些操作之間的關(guān)系,而不必知道數(shù)據(jù)的具體表示。

數(shù)據(jù)封裝

指把數(shù)據(jù)及其操作作為一個整體來進(jìn)行描述。

數(shù)據(jù)的具體表示對使用者是不可見的,對數(shù)據(jù)的只能通過封裝體所提供的操作來完成。

數(shù)據(jù)抽象與封裝是面象程序設(shè)計的基礎(chǔ)與基本理念。例:“棧”數(shù)據(jù)的表示與操作進(jìn)棧(push):增加一個元素退棧(pop):刪除一個元素后進(jìn)先出!例:“?!睌?shù)據(jù)的表示與操作

棧(stack)是一種由若干個具有線性次序的元素所構(gòu)成的復(fù)合數(shù)據(jù)。對棧只能實施兩種操作:

進(jìn)棧(push):增加一個元素

退棧(pop):刪除一個元素

上述兩個操作必須在棧的同一端(稱為棧頂,top)進(jìn)行。

后進(jìn)先出(Last

In

Out,簡稱LIFO)是棧的一個重要性質(zhì)。

push(...);

...pop(...);

...

;

push(x);

pop(y);

x

==

y棧:top“?!睌?shù)據(jù)的表示與操作--非數(shù)據(jù)抽象和封裝途徑

定義棧數(shù)據(jù)類型const

int

STACK_SIZE=100;struct

Stack{ int

top/;/空棧top==-1;滿棧top==STACK_SIZE-1int

buffer[STACK_SIZE];};棧:top

直接操作棧數(shù)據(jù)Stack

st;//定義棧數(shù)據(jù) int

x;

//對st進(jìn)行初始化。st.top=-1;//把12放進(jìn)棧。

st.top++;st.buffer[st.top]

=

12;//把棧頂元素存入變量x并退棧。x=st.buffer[st.top];st.top--;

存在的問題?

必需知道數(shù)據(jù)的表示

數(shù)據(jù)表示發(fā)生變化將影響操作

不安全top棧:

通過過程抽象操作棧數(shù)據(jù)bool

push(Stack

&s,int

i)

//增加一個元素i至棧{ if

(s.top

==STACK_SIZE-1) {

cout

<<

“Stack

is

overflow.\n”;

return

false;}else{

s.top++;

s.buffer[s.top]

=

i;return

true;}}bool

pop(Stack

&s,int

&i)

//從棧退出一個元素,賦值給i{ if

(s.top

==

-1){

cout

<<“Stack

isempty.\n”;return

false;}else{

i=

s.buffer[s.top];

s.top--;return

true;}}void

init(Stack

&s)

//初始化棧{ s.top

=

-1;}Stack

st;//定義棧數(shù)據(jù)int

x;init(st);

//對st進(jìn)行初始化。push(st,

12);

//把12放進(jìn)棧。pop(st,

x);

//把棧頂元素退棧并存入變量x。

存在的問題?

數(shù)據(jù)類型的定義與操作的定義是分開的,二者之間沒有顯式的聯(lián)系,push、pop在形式上與一般的函數(shù),如void

f(Stack

&s),沒有區(qū)別:

數(shù)據(jù)表示仍然是公開的,可以不通過push、pop來操作st:

在數(shù)據(jù)表示上直接操作

通過函數(shù)f來操作產(chǎn)生動力歌曲,控制空調(diào)、燈…承載,接受發(fā)動力、產(chǎn)生運動…改變傳動比,變速、變扭和傳遞或切斷發(fā)

輸出的動力車身底盤變速箱行車電腦及中控系統(tǒng)......組成:發(fā)功能:汽車數(shù)據(jù)

操作“?!睌?shù)據(jù)的表示與操作--數(shù)據(jù)抽象和封裝途徑

定義棧數(shù)據(jù)類型const

int

STACK_SIZE=100;class

Stack{int

top;int

buffer[STACK_SIZE];public:Stack()

{

top

=

-1;

}bool

push(int

i);bool

pop(int

&i);};bool

Stack::push(int

i)

//增加一個元素i至棧{ if

(top

==

-1){ cout

<<

“Stack

is

empty.\n”;return

false;}else{ i

=

buffer[top];

top--;return

true;}}{ if(top==STACK_SIZE-1)//良好的編程!判斷操作有效性{ cout

<<

“Stack

is

overflow.\n”;

return

false;

}else{

top++; buffer[top]

=

i;return

true;}}bool

Stack::pop(int

&i)

//從棧退出一個元素,賦值給i//良好的編程

!判斷操作有效性

使用棧類型數(shù)據(jù)Stack

st; //會自動地去調(diào)用st.Stack()對st進(jìn)行初始化。

int

x;st.push(12); //把12放進(jìn)棧st。st.pop(x); //把棧頂元素退棧并存入變量x。st.top

=

-1;

//Error

!st.top++;

//Error

!st.buffer[st.top]

=

12;

//Error

!st.f();

//Error

!兩種方案的不同

在方案(1)中,數(shù)據(jù)的表示對數(shù)據(jù)的使用者是公開的,對棧的操作可以直接在棧的數(shù)據(jù)表示上進(jìn)行,也可以通過所提供的函數(shù)來實現(xiàn);而在方案(2)中,只能通過提供的函數(shù)來操作棧。

在方案(1)中,數(shù)據(jù)和對數(shù)據(jù)操作的描述相互獨立,數(shù)據(jù)是作為參數(shù)傳給對數(shù)據(jù)進(jìn)行操作的函數(shù);而在方案(2)中,數(shù)據(jù)和對數(shù)據(jù)的操作構(gòu)成了一個整體,數(shù)據(jù)操作是數(shù)據(jù)定義的一部分。

方案(1)需要顯式地對棧進(jìn)行初始化,方案(2)則是隱式地(自動)進(jìn)行初始化。類

對象構(gòu)成了面象程序的基本計算單位,而對象的特征則由相應(yīng)的類來描述。類是對象的集合。

C++的類是一種用戶自定義類型,定義形式如下:class

<類名>{<成員描述>};其中,類的成員包括:

數(shù)據(jù)成員

成員函數(shù)

類成員標(biāo)識符的作用域為整個類定義范圍例:一個日期類的定義class

Date

{

private:int

year,month,day;//數(shù)據(jù)成員public:void

set(int

y,int

m,int

d)//成員函數(shù){

year

=

y;month

=

m;day

=

d;}bool

is_leap_year()//成員函數(shù){ return

(year%4

==

0

&&

year%100

!=

0)

||(year%400==0);}void

print()//成員函數(shù){ cout

<<

year

<<

"."

<<

month

<<

"."

<<day;}};描述三維幾何模型

數(shù)據(jù):

點、線、面

幾何屬性(法向、曲率)

拓?fù)潢P(guān)系

操作:、保存數(shù)據(jù)

幾何操作

打光照

貼紋理

…描述三維幾何的網(wǎng)格類class

CMesh

{

public:CMesh();virtual

~CMesh();private:int//數(shù)據(jù)成員m_nTriangle;*m_Triangle;m_nVertex;*m_Vertex;*m_Vnormal;TriangleintCoord3Coord3…

…public://成員函數(shù)方法SaveData(FILE

*fp);ReadData(FILE

*fp);DrawModel(int

RenderMode,

int

RenderModel);V3Standarize(Vector3

v);voidintintvoid…

…};數(shù)據(jù)成員

數(shù)據(jù)成員指類的對象所包含的數(shù)據(jù),它們可以是常量和變量格式相同,例。數(shù)據(jù)成員的說明格式與非成員數(shù)據(jù)的如:class

Date//類定義{

......private:

//

控制說明int

year,month,day;

//數(shù)據(jù)成員說明};

說明數(shù)據(jù)成員時不允許進(jìn)行初始化(某些靜態(tài)數(shù)據(jù)成員除外)。例如:class

A{ int

x=0; //Error

!const

double

y=0.0;......//Error

!};

數(shù)據(jù)成員的類型可以是任意的C++類型(void除外)//A是在程序其它地方定義的類,這里是

。

在說明一個數(shù)據(jù)成員的類型時,如果未見到相應(yīng)的類

型定義或相應(yīng)的類型未定義完,則該數(shù)據(jù)成員的類型只能是這些類型的指針或 類型(靜態(tài)成員除外)。例如:class

A;class

B{ A

a;//Error,未見A的定義。

B

b;//Error,B還未定義完。A*p;

//OKB

*q;

//OKA

&aa;

//OKB

&bb;

//OK};成員函數(shù)

成員函數(shù)描述了對類定義中的數(shù)據(jù)成員所能實施的操作。

成員函數(shù)的定義可以放在類定義中,例如:class

A{

...void

f(){...}//建議編譯器按內(nèi)聯(lián)函數(shù)處理。};

成員函數(shù)的定義也可以放在類定義外,例如:class

A{

...void

f();

//};void

A::f(){...}//需要用類名受限,區(qū)別于全局函數(shù)。當(dāng)該類的代碼量(變量、函數(shù))很多時,一般采用第二種描述三維幾何的網(wǎng)格類//

Mesh.h class

CMesh

{public:CMesh();virtual

~CMesh();private:

//數(shù)據(jù)成員m_nTriangle;*m_Triangle;m_nVertex;*m_Vertex;*m_Vnormal;intTriangleintCoord3Coord3…

…public:

//

成員函數(shù)方法

函數(shù)void

SaveData(FILE*fp);int

ReadData(FILE

*fp);int

DrawModel(int

RenderMode,int

RenderModel);void

V3Standarize(Vector3

v);…

…};//

Mesh.cpp:

implementation

of

the

CMesh

class....

...CMesh::CMesh(){ ...

...

}

CMesh::~CMesh(){ ...

...}int

CMesh::ReadData(FILE

*fp)//函數(shù)實現(xiàn){int

i,j;float

box[2][3];float

center[3];for

(i=0;i<m_nVertex;i++){fscanf(fp,"%f%f%f",

&(m_Vertex[i][0]),

&(m_Vertex[i][1]),

&(m_Vertex[i][2]));m_pProgress->SetPos(i);}...

...return

m_nTriangle;}void

CMesh::SaveData(FILE

*fp)//函數(shù)實現(xiàn){int

i;fprintf(fp,"%d

%d\n",m_nsVertex,m_nsTriangle);...

...}

類成員函數(shù)名是可以重載的(析構(gòu)函數(shù)除外),它遵循一般函數(shù)名的重載規(guī)則。例如:class

A{

......public:void

f();int

f(int

i);double

f(double

d);......};類成員的

控制

在C++的類定義中,可以用

控制修飾符public,private或protected來描述對類成員的

限制。例如:class

A不受限制。{

public:

//int

x;void

f();private://只能在本類和

int

y;的代碼中。的代碼中。void

g();protected://只能在本類、派生類和

int

z;void

h();};

在C++的類定義中,默認(rèn)

控制是private,并可以有多個public、private和protected

控制說明,例如:class

A{ int

m,n;//m,n的public:int

x;void

f();private:int

y;void

g();protected:int

z;void

h();public:void

f1();控制說明為private。};

一般來說,類的數(shù)據(jù)成員和在類的的成員函數(shù)應(yīng)該指定為private,只有提供給外界使用的成員函數(shù)才指定為public。

具有public

控制的成員構(gòu)成了類與外界的一種接口(interface)。操作一個類的對象時,只能通過來實現(xiàn)。

protected類成員(繼承)。對象類中的public成員控制具有特殊的作用用鏈表實現(xiàn)“?!鳖怱tack#include

<iostream>#include

<cstdio>using

namespace

std;class

Stack{ public://對外的接口

Stack(){top=NULL;}

bool

push(int

i);bool

pop(int&

i);private:struct

Node{ int

content;}

*top;Node

*next;};//增加一個元素i至棧bool

Stack::push(int

i){Node

*p=new

Node;if

(p

==NULL)cout

<<

"Stack

isoverflow.\n";return

false;{}else{

p->content

=

i;p->next

=

top;return

true;top

=

p;}}bool

Stack::pop(int&

i)//從棧退出一個元素,賦值給i{ if

(top

==

NULL){ cout

<<

"Stack

isempty.\n";return

false;}{else

Node

*p=top;//i

=

top->content;top

=

top->next;

//

top

=

top->next;i

=

p->content;delete

p;

return

true;}}對

類屬于類型范疇的程序?qū)嶓w,它一般存在于靜態(tài)的程序(運行前的程序)中。

而動態(tài)的面

象程序(運行中的程序)則是由對象構(gòu)成。類是對象的抽象化,對象是類的具體化學(xué)生類:教師類:、、(對象)、路通

、 (對象)汽車類:大眾甲殼蟲、奧迪A6、寶馬X3(對象)點心類:面包、餅干、切糕(對象)對象在程序運行時創(chuàng)建,程序的執(zhí)行是通過對象之間相互發(fā)送消息來實現(xiàn)的。當(dāng)對象接收到一條消息后,它將調(diào)用對象類中定義的某個成員函數(shù)來處理這條消息。對象的創(chuàng)建和標(biāo)識

直接方式

通過在程序中定義一個類型為類的變量來實現(xiàn)的,其格式與普通變量的定義相同。例如:class

A{

public:void

f();void

g();private:int

x,y;}......A

a1;

//創(chuàng)建一個A類的對象。A

a2[100];//創(chuàng)建由對象數(shù)組表示的100個A類對象。

對象通過對象名來標(biāo)識和

。

分為:全局對象、局部對象和成員對象。

間接方式(動態(tài)對象)

在程序運行時刻,通過new操作或malloc庫函數(shù)來創(chuàng)建對象。

所創(chuàng)建的對象稱為動態(tài)對象,其內(nèi)存空間在程序的堆區(qū)中。

動態(tài)對象用delete操作或free庫函數(shù)來撤消(使之消亡)。

動態(tài)對象通過指針來標(biāo)識和

。單個動態(tài)對象的創(chuàng)建與撤消A

*p;p=new

A;

//創(chuàng)建一個A類的動態(tài)對象。動態(tài)對象……//通過pif

(p)delete

p;//撤消p所指向的動態(tài)對象?;騪

=

(A

*)malloc(sizeof(A));......free(p);動態(tài)對象數(shù)組的創(chuàng)建與撤消或p

=

(A

*)malloc(sizeof(A)*100);......free(p);A

*p;p=new

A[100];

//創(chuàng)建一個動態(tài)對象數(shù)組。......//通過pif

(p)delete

[]p;動態(tài)對象數(shù)組//撤消p所指向的動態(tài)對象數(shù)組。

對于動態(tài)對象,需注意下面幾點:

new操作自動分配空間、自動返回指定類型的指針。

用new創(chuàng)建的動態(tài)對象數(shù)組只能用默認(rèn)構(gòu)造函數(shù)進(jìn)行初始化。delete中的“[]”不能省,否則,只有第一個對象的析構(gòu)函數(shù)會被調(diào)用。

函數(shù)malloc不會調(diào)用對象類的構(gòu)造函數(shù);函數(shù)free也不會調(diào)對象類的析構(gòu)函數(shù)。后兩周課程及考試安排

本周五12月28號1-2節(jié)(郭)

本周五12月28日7-8節(jié)(郭)4:10-6:00其中5:00-5:30課堂測驗函數(shù)

本周六12月29號3-4節(jié)(郭,補(bǔ)下周三課)

下周一12月31日7-10節(jié)上機(jī)實驗

下周五1月4日1-2節(jié)習(xí)題課(劉)

下周五1月4日7-8節(jié)最后一次上機(jī)測驗

元月20號上午8:00-10:00:期末閉卷筆試產(chǎn)生動力歌曲,控制空調(diào)、燈…承載,接受發(fā)動力、產(chǎn)生運動…改變傳動比,變速、變扭和傳遞或切斷發(fā)

輸出的動力車身底盤變速箱行車電腦及中控系統(tǒng)......組成:發(fā)功能:汽車數(shù)據(jù)

操作描述三維幾何模型

數(shù)據(jù):

點、線、面

幾何屬性(法向、曲率)

拓?fù)潢P(guān)系

操作:、保存數(shù)據(jù)

幾何操作

打光照

貼紋理

…描述學(xué)生

數(shù)據(jù):、

、

、籍貫、成績、

操作:

保存、導(dǎo)出數(shù)據(jù)

計算平均成績

…類

對象構(gòu)成了面象程序的基本計算單位,而對象的特征則由相應(yīng)的類來描述。類是對象的集合。

C++的類是一種用戶自定義類型,定義形式如下:class

<類名>{<成員描述>};其中,類的成員包括:

數(shù)據(jù)成員

成員函數(shù)

類成員標(biāo)識符的作用域為整個類定義范圍數(shù)據(jù)成員

數(shù)據(jù)成員指類的對象所包含的數(shù)據(jù),它們可以是常量和變量格式相同,例。數(shù)據(jù)成員的說明格式與非成員數(shù)據(jù)的如:class

Date//類定義{

......private:

//

控制說明int

year,month,day;

//數(shù)據(jù)成員說明};

說明數(shù)據(jù)成員時不允許進(jìn)行初始化(某些靜態(tài)數(shù)據(jù)成員除外)。例如:class

A{ int

x=0; //Error

!const

double

y=0.0;......//Error

!};成員函數(shù)

成員函數(shù)描述了對類定義中的數(shù)據(jù)成員所能實施的操作。

成員函數(shù)的定義可以放在類定義中,例如:class

A{

...void

f(){...}//建議編譯器按內(nèi)聯(lián)函數(shù)處理。};

成員函數(shù)的定義也可以放在類定義外,例如:class

A{

...void

f();

//};void

A::f(){...}//需要用類名受限,區(qū)別于全局函數(shù)。當(dāng)該類的代碼量(變量、函數(shù))很多時,一般采用第二種例:一個日期類的定義class

Date

{

private:int

year,month,day;//數(shù)據(jù)成員public:void

set(int

y,int

m,int

d)//成員函數(shù){

year

=

y;month

=

m;day

=

d;}bool

is_leap_year()//成員函數(shù){ return

(year%4

==

0

&&

year%100

!=

0)

||(year%400==0);}void

print()//成員函數(shù){ cout

<<

year

<<

"."

<<

month

<<

"."

<<day;}};描述三維幾何的網(wǎng)格類//

Mesh.h class

CMesh

{public:

//

不受限制。CMesh();virtual

~CMesh();private:

//

數(shù)據(jù)成員

//只能在本類和

的代碼中

。m_nTriangle;*m_Triangle;m_nVertex;*m_Vertex;*m_Vnormal;intTriangleintCoord3Coord3…

…成員函數(shù)

方法

函數(shù)SaveData(FILE

*fp);ReadData(FILE

*fp);DrawModel(int

RenderMode,

int

RenderModel);V3Standarize(Vector3

v);…

//只能在本類、派生類和

的代碼中

。public:

//voidintintvoidprotected:};//

Mesh.cpp:

implementation

of

the

CMesh

class....

...CMesh::CMesh()

//構(gòu)造函數(shù){ ...

...

}

CMesh::~CMesh()

//析構(gòu)函數(shù){ ...

...}int

CMesh::ReadData(FILE

*fp)//函數(shù)定義{int

i,j;float

box[2][3];float

center[3];for

(i=0;i<m_nVertex;i++){fscanf(fp,"%f%f%f",

&(m_Vertex[i][0]),

&(m_Vertex[i][1]),

&(m_Vertex[i][2]));m_pProgress->SetPos(i);}...

...return

m_nTriangle;}void

CMesh::SaveData(FILE

*fp)//函數(shù)定義{int

i;fprintf(fp,"%d

%d\n",m_nsVertex,m_nsTriangle);...

...}VS

開發(fā)環(huán)境添加類對象的創(chuàng)建和標(biāo)識

class

A

{…

…};......//直接方式:A

a1;

//創(chuàng)建一個A類的對象。A

a2[100];//創(chuàng)建由對象數(shù)組表示的100個A。//間接方式:動態(tài)對象或動態(tài)對象數(shù)組A

*p=new

A;

//創(chuàng)建一個A類的動態(tài)對象。動態(tài)對象……//通過pif(p)delete

p;

//撤消p所指向的動態(tài)對象。對象的操作

對于創(chuàng)建的一個對象,需要通過向它發(fā)送消息來對它進(jìn)行操作(

象發(fā)送消息指:

調(diào)用對象類中定義的某個public成員函數(shù))。例如:class

A{ int

x;public:void

f();};int

main(){ A

a;a.f();//創(chuàng)建A類的一個局部對象a。//調(diào)用A類的成員函數(shù)f對對象a進(jìn)行操作。A

*p=new

A;

//創(chuàng)建A類的一個動態(tài)對象,p指向之。p->f();

//調(diào)用A類的成員函數(shù)f對p所指向的對象進(jìn)行操作。delete

p;return

0;}

通過對象來

類的成員時要受到類成員

控制的限制,例如::x,y,f,g,hclass

A{

public:void

f(){......//允許}private:int

x;void

g(){......//允許}protected:int

y;void

h();};:x,y,f,g,hvoid

A::h(){......//允許A

a;...//能}:x,y,f,g,ha.x、a.y、a.g和a.h嗎?void

func(){ A

a;a.f();

//OKa.x

=

1;

//Errora.g();

//Errora.y

=

1;

//Errora.h();

//Error......}

可以對同類對象進(jìn)行賦值Date

yesterday,

today,

some_day;some_day=yesterday;//把對象yesterday的數(shù)據(jù)成員分別

//賦值給對象some_day的相應(yīng)數(shù)據(jù)成員。

取對象地址Date

*p_date;p_date=&today;

//把對象today的地址賦值給對象指針p_date。

把對象作為實參傳給函數(shù)以及作為函數(shù)的返回值等操作Date

f(Date

d){ Date

x;......return

x;}some_day2=f(yesterday);//調(diào)用函數(shù)f,把對象yesterday//作為實參。返回值對象賦給對象some_day2。思考:直接進(jìn)行賦值有問題嗎?良好的編程

變量初始化(賦初值):初始化自定義變量是一種良好的編程

,是為了使程序更健壯,有助于減少Bugs的可能;使變量有一個可知的確定的值,如果沒有初始化會,當(dāng)程序執(zhí)行時可能出現(xiàn)未知或不可預(yù)料的錯誤。例如:int

nAge

=

0;char

cSex

=

'\0',

cMs

=

'\0';初始化一個數(shù)組或矩陣對象的初始化:構(gòu)造函數(shù)

當(dāng)一個對象被創(chuàng)建時,它將獲得一塊

空間,該

空間用于

對象的數(shù)據(jù)成員。

在使用對象前,需要對對象

空間中的數(shù)據(jù)成員進(jìn)行初始化。

C++提供了一種對象初始化的機(jī)制:構(gòu)造函數(shù)(Constructor)。

構(gòu)造函數(shù)是類的特殊成員函數(shù),它的名字與類名相同、無返回值類型。創(chuàng)建對象時,構(gòu)造函數(shù)會自動被調(diào)用。例如:

class

A{ int

x;public:A(){x=0;}//構(gòu)造函數(shù)......};......A

a;//創(chuàng)建對象a:為a分配內(nèi)存空間,然后調(diào)用a的構(gòu)造函數(shù)A()。

構(gòu)造函數(shù)可以重載,其中,不帶參數(shù)的(或所有參數(shù)都有默認(rèn)值的)構(gòu)造函數(shù)被稱為默認(rèn)構(gòu)造函數(shù)。例如:class

A{ int

x,y;public:A()//默認(rèn)構(gòu)造函數(shù){

x

=

y

=

0;}A(int

x1){ x

=

x1;

y

=

0;}A(int

x1,int

y1){ x

=x1;

y

=

y1;}......};如果類中未提供任何構(gòu)造函數(shù),則編譯程序在需要時會隱式地為之提供一個默認(rèn)構(gòu)造函數(shù)。

在創(chuàng)建對象時,可以顯式地指定調(diào)用對象類的某個構(gòu)造函數(shù)。

如果沒有指定調(diào)用何種構(gòu)造函數(shù),則調(diào)用默認(rèn)構(gòu)造函數(shù)初始化。class

A{

......

public:

A();A(int

i);A(char

*p);};......A

a1;//調(diào)用默認(rèn)構(gòu)造函數(shù)。也可寫成:A

a1=A();//但不能寫成:A

a1();A

a2(1);

//調(diào)用A(int

i)。也可寫成:A

a2=A(1);或A

a2=1;A

a3("abcd");//調(diào)A(char

*)。也可寫成:A

a3=A("abcd");//或A

a3="abcd";A

a[4];

//調(diào)用對象a[0]、a[1]、a[2]、a[3]的默認(rèn)構(gòu)造函數(shù)。class

A{

......

public:

A();A(int

i);A(char

*p);};......A

b[5]={A(),A(1),A("abcd"),2,"xyz"};//調(diào)用b[0]的A()、//b[1]的A(int)、b[2]的A(char

*)、b[3]的A(int)和b[4]的A(char

*)A

*p1=new

A;

//調(diào)用默認(rèn)構(gòu)造函數(shù)。A

*p2=new

A(2);//調(diào)用A(int

i)。A

*p3=new

A("xyz");

//調(diào)用A(char

*)。A

*p4=new

A[20];

//創(chuàng)建動態(tài)對象數(shù)組時只能調(diào)用各對象的//默認(rèn)構(gòu)造函數(shù)成員初始化表

對于常量數(shù)據(jù)成員和數(shù)據(jù)成員(某些靜態(tài)成員除外),不能在說明它們時初始化,也不能在構(gòu)造函數(shù)中采用賦值操作對它們初始化。例如:

class

A{ int

x;const

int

y=1;

//Error//Errorint

&z=x;public:A(){ x=

0;//OKy

=

1;

//Errorz

=

&x;

//Error}};

對于常量數(shù)據(jù)成員和數(shù)據(jù)成員,可以在定義構(gòu)造函數(shù)時,在函數(shù)頭和函數(shù)體之間加入一個成員初始化表來對它們進(jìn)行初始化。例如:class

A{ int

x;const

int

y;int&

z;public:A():z(x),y(1)

//成員初始化表{

x

=

0;}};

成員初始化表中成員初始化的書寫次序并不決定它們的初始化次序,數(shù)據(jù)成員的初始化次序由它們在類定義中的說明次序來決定。

在類中可以定義一個特殊的成員函數(shù):析構(gòu)函數(shù)

(Destructor),它的名字為“~<類名>”,沒有返回類型、不帶參數(shù)、不能被重載。例如:class

String{ int

len;char

*str;public:String(char*

s);~String();}

一個對象消亡時,系統(tǒng)在收回它的內(nèi)存空間之前,將會自動調(diào)用析構(gòu)函數(shù)。

可以在析構(gòu)函數(shù)中完成對象被刪除前的一些清理工作(如:歸還對象額外申請的資源等)。析構(gòu)函數(shù)–打掃戰(zhàn)場class

String{ int

len;char

*str;public:String(char*

s){ len

=

strlen(s);str=new

char[len+1];//申請資源strcpy(str,s);}~String(){

if

(str)delete[]str;//歸還資源}};void

f(){

String

s1("abcd");//調(diào)用s1的構(gòu)造函數(shù)......}//調(diào)用s1的析構(gòu)函數(shù)

注意:系統(tǒng)為對象s1分配的內(nèi)存空間只包含len和str(指針)本身所需的空間,str所指向的空間不由系統(tǒng)分配,而是由對象

(new)自己處理!s1len:str:4abcd

析構(gòu)函數(shù)可以顯式調(diào)用s1.~String();

//只歸還對象的資源,//對象并未消亡!成員對象

對于類的數(shù)據(jù)成員,其類型可以是另一個類。也就是說,一個對象可以包含另一個對象(稱為成員對象)。例如:class

A{

...};class

B{...A

a;//成員對象...};B

b;//對象b包含一個成員對象:b.a成員對象的初始化

成員對象由成員對象類的構(gòu)造函數(shù)初始化:

如果在包含成員對象的類中,沒有

用成員對象類的什么構(gòu)造函數(shù)對成員對象初始化,則調(diào)用成員對象類的默認(rèn)構(gòu)造函數(shù)。

可以在類構(gòu)造函數(shù)的成員初始化表中顯式

用成員對象類的某個構(gòu)造函數(shù)對成員對象初始化。class

A{ int

x;public:A(){

x

=

0;

}A(int

i)

{

x

=

i;

}};class

B{ Aa;int

y;public:B(int

i){y=i;}//調(diào)用A的默認(rèn)構(gòu)造函數(shù)對a初始化。

B(int

i,

intj):a(j){y=i;}//調(diào)用A(int)對a

初始化。};B

b1(1);//b1.y初始化為1,b1.a.x初始化為0B

b2(1,2);//b2.y初始化為1,b2.a.x初始化為2

創(chuàng)建包含成員對象的類的對象時,先執(zhí)行成員對象類的構(gòu)造函數(shù),再執(zhí)行本身類的構(gòu)造函數(shù)。

一個類若包含多個成員對象,這些對象的初始化次序按它們在類中的說明次序(而不是成員初始化表的次序)進(jìn)行。

析構(gòu)函數(shù)的執(zhí)行次序與構(gòu)造函數(shù)的執(zhí)行次序正好相反??截悩?gòu)造函數(shù)

在創(chuàng)建一個對象時,若用一個同類型的對象對其初始化,這時將會調(diào)用一個特殊的構(gòu)造函數(shù):拷貝構(gòu)造函數(shù)。例如:class

A{

......public:A();

//默認(rèn)構(gòu)造函數(shù)A(const

A&

a);

//拷貝構(gòu)造函數(shù)};

在三種情況下,會調(diào)用類的拷貝構(gòu)造函數(shù):

定義對象時,例如:A

a1;A

a2(a1);

//也可寫成:A

a2=a1;或:A

a2=A(a1);//調(diào)用A的拷貝構(gòu)造函數(shù),用對象a1初始化對象a2,

把對象作為值參數(shù)傳給函數(shù)時,例如:void

f(A

x);A

a;f(a);//調(diào)用f時將創(chuàng)建形參對象x,并調(diào)用A的拷貝構(gòu)造函數(shù),//用對象a對其初始化。

把對象作為函數(shù)的返回值時,例如:A

f(){ A

a;......return

a;//創(chuàng)建一個A類的臨時對象,并調(diào)用A的//拷貝構(gòu)造函數(shù),用對象a對其初始化。}隱式拷貝構(gòu)造函數(shù)

如果程序中沒有為類提供拷貝構(gòu)造函數(shù),則編譯器將會為其生成一個隱式拷貝構(gòu)造函數(shù)。

隱式拷貝構(gòu)造函數(shù)將逐個成員拷貝初始化:

對于普通成員:它采用通常的初始化操作;

對于成員對象:則調(diào)用成員對象類的拷貝構(gòu)造函數(shù)來實現(xiàn)成員對象的初始化。class

A{ int

x,

y;public:A()

{x=0; y=2;

}void

inc(){

x++;y++;}......//其中沒有定義拷貝構(gòu)造函數(shù)。};A

a1;a1.inc();A

a2(a1);//a2.x初始化為1(a1.x);//a2.y初始化為3(a1.y)class

A{ int

x,y;public: A()

{

x

=

y

=

0;

}

......};class

B{ int

z;A

a;public:B()

{

z

=

0;

}......//其中沒有定義拷貝構(gòu)造函數(shù)};...B

b1;B

b2(b1);//b1.z、b1.a.x以及b1.a.y均為0。//b2.z初始化成b1.z;調(diào)用A的拷貝構(gòu)造函數(shù)用b1.a對//b2.a初始化。如果A中沒有定義拷貝構(gòu)造函數(shù),則//A的隱式拷貝構(gòu)造函數(shù)把b2.a.x和b2.a.y分別初始化成//b1.a.x和b1.a.y;否則,由A的自定義拷貝構(gòu)造函數(shù)決定//如何對b2.a.x和b2.a.y進(jìn)行初始化。自定義拷貝構(gòu)造函數(shù)

一般情況下,編譯程序提供的默認(rèn)拷貝構(gòu)造函數(shù)的行為足以滿足要求,類中不需要自定義拷貝構(gòu)造函數(shù)。在有些情況下必須要自定義拷貝構(gòu)造函數(shù),否則,將會產(chǎn)生設(shè)計者未

的嚴(yán)重的程序錯誤。class

A{ int

x,y;char

*p;

public:

A(char

*str){ x

=

0; y

=

0;p

=

new

char[strlen(str)+1];strcpy(p,str);}};xypxyp~A()

{

delete[]

p;

p=NULL;

}a1a2abcd0000......A

a1(“abcd”);A

a2(a1);系統(tǒng)提供的隱式拷貝構(gòu)造函數(shù)將會使得a1和a2的成員指針p指向同一塊內(nèi)存區(qū)域!

它帶來的問題是:

如果對一個對象操作之后修改了這塊空間的內(nèi)容,則另一個對象將會受到影響。如果不是設(shè)計者特意所為,這將是一個隱藏的錯誤。

當(dāng)對象a1和a2消亡時,將會分別去調(diào)用它們的析構(gòu)函數(shù),這會使得同一塊內(nèi)存區(qū)域?qū)⒈粴w還兩次,從而導(dǎo)致程序運行異常。

解決上面問題的辦法是在類A中顯式定義一個拷貝構(gòu)造函數(shù)A::A(const

A&

a){ x

=

a.x;y

=a.y;p

=

new

char[strlen(a.p)+1];strcpy(p,a.p);}xypxypa1a2abcd0000abcd再回顧關(guān)于結(jié)構(gòu)體變量的賦值struct

Vect{int

n;int

*p;}A;cin>>A.n;該程序有問題嗎?B.p=A.p;B.p=new

int[B.n];for

(int

i=0;i<A.n;

i++)B.p[i]

=A.p[i];函數(shù)A.p=new

int[A.n/]/;以上可以寫成個…

…//

Vect

B=A;if

(A.p)delete

[]A.p;if

(B.p)delete

[]B.p;

自定義拷貝構(gòu)造函數(shù)可以對初始化的行為進(jìn)行控制。例如:class

A{ int

x,y;public:A()

{

x

=

0; y

=

2;}A(const

A&

a){ x

=

a.x+1;y

=

a.y+1;}};......A

a1;

//a1初始化為:a1.x=0,a1.y=2A

a2(a1);//a2初始化為:a2.x=1,a2.y=3

自定義的拷貝構(gòu)造函數(shù)將默認(rèn)調(diào)用成員對象類的默認(rèn)構(gòu)造函數(shù)對成員對象初始化!class

A{ int

x,y;public:A()

{

x

=

0; y

=

2;}void

inc()

{

x++;

y++;

}};{

z

=

b.z;

}B

b1;B

b2(b1)

如何能讓b2與b1一致呢?class

B{ intz;A

a;public:B()

{z

=

0;

}B(const

B&

b):

a(b.a)void

inc()

{z++;a.inc();

}};...//b1.a.x為0、b1.a.y為2、b1.z為0

。b1.inc();

//b1.a.x為1、b1.a.y為3、b1.z為1。//b2.a.x為1、b2.a.y為3、b2.z為1。const成員函數(shù)

在程序運行的不同時刻,一個對象可能會處于不同的狀態(tài)(由對象的數(shù)據(jù)成員的值來體現(xiàn))。

可以把類中的成員函數(shù)分成兩類:

獲取對象狀態(tài)

改變對象狀態(tài)例如class

Date{

public:void

set(int

y,

int

m,

int

d)//改變對象狀態(tài)

int

get_day();//獲取對象狀態(tài)int

get_month();//獲取對象狀態(tài)

int

get_year();//獲取對象狀態(tài)......};

為了防止在獲取對象狀態(tài)的成員函數(shù)中改變對象的狀態(tài),

可以把它們說明成const成員函數(shù)。

const成員函數(shù)不能改變對象的狀態(tài)(數(shù)據(jù)成員的值)。例如:class

A{ int

x;char

*p;public:......void

f()

const{ x

=

10;

//Errorp

=

new

char[20];

//Errorstrcpy(p,"ABCD");//沒有改變p的值,編譯程序認(rèn)為OK!}};

const修飾符還有一個作用:修飾常量對象,限制對常量對象的操作。例如:class

Date{ int

d,m,y;public:Date();Date(int

year,

int

month,

int

day);int

get_day()

const

{

return

d;

}int

get_month()

const

{

return

m;

}int

get_year()

const

{

return

y;

}int

set

(int

year,

int

month,

int

day){ y

=

year;

m

=

month;

d

=

day;}};void

f(const

Date

&d){

cout

<<d.get_day()

<<

d.get_month()

<<

d.get_year();d.set

(2011,3,23);

//Error}同類對象共享數(shù)據(jù)

屬于同一個類的不同對象有時需要共享數(shù)據(jù)。

采用全局變量來表示共享數(shù)據(jù)違背數(shù)據(jù)抽象與封裝原則,數(shù)據(jù)缺乏保護(hù)。int

x;//被A類對象共享的全局變量class

A{

......{

x++;

......

}void

f()}

a,b;a.f();b.f();//上述操作對同一個x進(jìn)行x++;

//不通過A類對象也能

x!回顧:舉例關(guān)于staticvoid

f(){

auto

int

x=0;//auto一般不寫

static

inty=1;register

int

z=0;x++;

y++;z++;cout

<<

x

<<

y

<<

z

<<

endl;}//在main函數(shù)中調(diào)用f();

第一次調(diào)用f時,輸出:1

2

1

第二次調(diào)用f時,輸出:1

3

1

可通過靜態(tài)數(shù)據(jù)成員來實現(xiàn)屬于同一個類的不同對象之間的數(shù)據(jù)共享:class

A{

......static

int

x;//靜態(tài)數(shù)據(jù)成員{

x++;......

}void

f()}

a,b;a.f();b.f();//上述操作對同一個x進(jìn)行x++;

//Error,不通過A類對象不能

x!

在類中,可以把數(shù)據(jù)成員說明成靜態(tài)的。例如:class

A{ int

x,y;static

intshared;

//靜態(tài)數(shù)據(jù)成員說明

public:A()

{

x

=

y

=

0;}void

increase_all()

{

x++;

y++;

shared++;

}int

sum_all()

const

{

return

x+y+shared;

}......};int

A::shared=0;

//靜態(tài)數(shù)據(jù)成員的定義靜態(tài)數(shù)據(jù)成員

類的靜態(tài)數(shù)據(jù)成員對該類的所有對象只有一個拷貝。例如:A

a1,a2;shared:

0a1a2a1.x:a1.y:00a2.x:a2.y:

00

類的靜態(tài)數(shù)據(jù)成員對該類的所有對象只有一個拷貝。例如:A

a1,a2;shared:

1a1a20a1.x:

a2.x:a1.y:11a2.y:

0a1.increase_all();cout<<a2.sum_all()<<endl;

//輸出:1靜態(tài)成員函數(shù)

成員函數(shù)也可以

成靜態(tài)的。class

A{ int

x,y;static

int

shared;public:A()

{

x

=

y

=

0;

}static

int

get_shared()

{

return

shared;

}......};int

A::shared=0;

靜態(tài)成員函數(shù)只能類的靜態(tài)成員。

靜態(tài)成員函數(shù)可以通過對象來

外,也可以直接通過類來

。例如:A

a;cout<<a.get_shared();//也可以是:A::get_shared();

classA

{

static

int

obj_count;…...public:A()

{obj_count++;}~A()

{

obj_count--;

}static

int

get_num_of_objects(){

return

obj_count;}…...};int

A::obj_count=0;......cout

<<

A::get_num_of_objects()

<<

endl;例:實現(xiàn)對某類對象的計數(shù)類成員的

控制

在類定義中,用

控制修飾符public,private,protected來描述對類成員的

限制。

例如:class

A{

public:

//int

x;void

f();不受限制。的代碼中private://只能在本類和

int

y;void

g();。的代碼中protected://只能在本類、派生類和

int

z;void

h();。};某些全局函數(shù)某些其它類某些其它類的某......class

A類成員函數(shù){

......friend

void

func();

//

函數(shù)friend

class

B;

//

類friend

void

C::f();

//些成員函數(shù)};

為了提高在類的外部對類的數(shù)據(jù)成員的

效率,在C++中,可以指定與一個類密切相關(guān)的、又不適合作為該類成員的程序?qū)嶓w可以直接 該類的private和protected成員,這些程序?qū)嶓w稱為該類的 。例如:所謂外地,就是作為一個類的“朋友”,可以例它的私有成員數(shù)據(jù)或私有成員函數(shù)。關(guān)于

的幾點說明是數(shù)據(jù)保護(hù)和數(shù)據(jù)

效率之間的一種折衷方案。關(guān)系具有不對稱性。例如:假設(shè)B是A的,如果沒有顯式A是B的,則A不是B的。也不具有傳遞性。例如:假設(shè)B是A的、C是B的

,如果沒有顯式

C是A的

,則C不是A的

。類應(yīng)用舉例用類來實現(xiàn)矩陣和向量類型定義:矩陣類、向量類數(shù)據(jù):行數(shù)、列數(shù)、數(shù)據(jù)……具體操作:矩陣、向量的元素實現(xiàn)乘法……例:用類來實現(xiàn)矩陣和向量類型class

Matrix

//矩陣類{ int

*p_data; //表示矩陣數(shù)據(jù)int

row,col; //表示矩陣的行數(shù)和列數(shù)

public:Matrix(int

r,

int

c){ if

(r

<=

0

||

c

<=

0){ cerr<<"矩陣尺寸不合法!\n";exit(-1);}row

=

r; col

=

c;p_data

=

new

int[row*col];for

(int

i=0;

i<row*col;

i++)

p_data[i]

=

0;}~Matrix(){delete

[]p_data;}int

&element(int

i,

int

j)

//矩陣元素。{ if

(i

<

0

||

i

>=

row

||

j

<

0

||

j

>=

col){ cerr<<"矩陣下標(biāo)越界\n";exit(-1);}return

*(p_data+i*col+j);}in ement(int

i,

int

j)

const//

矩陣元素(為常量對象而提供)。{ if

(i

<

0

||i

>=

row

||

j

<

0

||

j

>=

col){ cerr<<"矩陣下標(biāo)越界\n";exit(-1);}return

*(p_data+i*col+j);}Matrix

m(10,10);......m.element(1,2)

=m.element(1,2)+1;void

f(const

Matrix&

m){

int

x;x

=m.element(1,2)*10+1;}int

dimension_row()const//獲得矩陣的行數(shù)。{ return

row;

}

int

dimens

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論