C++課件第7 章類與對(duì)象_第1頁
C++課件第7 章類與對(duì)象_第2頁
C++課件第7 章類與對(duì)象_第3頁
C++課件第7 章類與對(duì)象_第4頁
C++課件第7 章類與對(duì)象_第5頁
已閱讀5頁,還剩44頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第7章類與對(duì)象

7.1類與對(duì)象的定義與使用

一、類和對(duì)象的定義

類可以理解為用戶定義的數(shù)據(jù)類型,而“int”和“double”是系統(tǒng)定義的簡單數(shù)據(jù)類型。

變量是簡單數(shù)據(jù)類型的實(shí)例,對(duì)象是類的實(shí)例。

一個(gè)對(duì)象由幾個(gè)簡單數(shù)據(jù)類型的變量,和幾個(gè)函數(shù),封裝在一起組成。把數(shù)據(jù)和函數(shù)封裝在

一起組成對(duì)象,是面向?qū)ο蠹夹g(shù)的基本特征。

用成員訪問符訪問對(duì)象中的成員。也可以用指向?qū)ο蟮闹羔樤L問,此時(shí)”(*p).a”等效于

例:復(fù)數(shù)類

classcomplex

(

private:〃復(fù)數(shù)的實(shí)部和虛部

doublereal;

doubleimage;

public:

voidset(doubler,doublei)〃共有函數(shù)成員,設(shè)定復(fù)數(shù)值

(

real=r;

image=i;

)

voiddislayO〃輸出復(fù)數(shù)值

(

cout?real?',+M?image?,,i',?endl;

)

};

voidmain()

(

complexx,y,*p;

x.set(l,3);〃調(diào)用對(duì)象x的成員函數(shù),設(shè)定復(fù)數(shù)值

y.set(3,5);

x.dislayO;

y.dislayO;

p=&x;

(*p).set(6,6);

p->dislay();

cout?x.real;〃錯(cuò)誤,私有成員只能被complex類的成員函數(shù)訪問,不能被main函數(shù)

訪問

注意:

1.定義類時(shí),用關(guān)鍵字“public""private”"protected”來表示各數(shù)據(jù)成員和函數(shù)成員的訪問權(quán)

限。公有權(quán)限的成員,在程序的任何位置都可以被訪問:私有和保護(hù)權(quán)限的成員,只有本類

(不是本對(duì)象)的成員函數(shù)可以訪問。

2.用關(guān)鍵字“class”和“struct”都可以定義類,區(qū)別在于,不指定訪問權(quán)限的情況下,“struct”

默認(rèn)公有,包括公有成員和公有繼承;“class”默認(rèn)私有,包括私有成員和私有繼承。

3.類的定義只是說明了?組對(duì)象的共同特征,所以類定義中說明的數(shù)據(jù)成員并不是具體的

變量,沒有分配內(nèi)存空間,也不能賦初值。定義對(duì)象的時(shí)候,才會(huì)把類實(shí)例化,對(duì)象的數(shù)據(jù)

成員有對(duì)應(yīng)的內(nèi)存空間。

4.類定義結(jié)尾有分號(hào)

二、定義對(duì)象的其他方法

I.定義類的同時(shí)定義對(duì)象

classcomplex

(

private:〃復(fù)數(shù)的實(shí)部和虛部

doublereal;

doubleimage;

public:

voidset(doubler,doublei)〃共有函數(shù)成員,設(shè)定復(fù)數(shù)值

(

real=r;

image=i;

)

voiddislay()〃輸出復(fù)數(shù)值

(

cout?real?"+"?image?"i"?endl;

)

}x,y;

定義類以后立刻定義了對(duì)象,因?yàn)轭惗x是放在全局位置,這里的“x”和“尸兩個(gè)對(duì)象也是全

局變量。

2.不出現(xiàn)類名定義對(duì)象

class

(

private:〃復(fù)數(shù)的實(shí)部和虛部

doublereal;

doubleimage;

public:

voidset(doubler,doublei)〃共有函數(shù)成員,設(shè)定復(fù)數(shù)值

real=r;

image=i;

voiddislayO〃輸出復(fù)數(shù)值

cout?real?',+"?image?,'i,'?endl;

}x,y;

因?yàn)轭悰]有名字,以后不能再定義該類的對(duì)象,這個(gè)類只有“x”和“y”兩個(gè)對(duì)象。

三、定義類時(shí),可以把成員函數(shù)的實(shí)現(xiàn)代碼放到類外

classcomplex

(

private:

doublereal;

doubleimage;

public:

voidset(doubler,doublei);

voiddislayO;

);

voidcomplex::set(doubleredoublei)

(

real=r;

image=i;

)

voidcomplex::dislay()

(

cout?real?"+"?image?"i"?endl;

)

注:

L"::“是作用域操作符,函數(shù)名前加“complex::"表示“set”和“dislay”函數(shù)是類“complex”的成員

函數(shù)。

2.當(dāng)類的成員函數(shù)實(shí)現(xiàn)代碼寫在類內(nèi)部時(shí),這個(gè)函數(shù)被默認(rèn)定義成內(nèi)聯(lián)函數(shù)。實(shí)現(xiàn)代碼寫

在類外部的函數(shù),要想定義成內(nèi)聯(lián)函數(shù),需加關(guān)鍵字“inline”。

7.2類的特殊成員

一、this指針

在對(duì)象調(diào)用自己的非靜態(tài)成員函數(shù)時(shí),會(huì)自動(dòng)傳給成員函數(shù)一個(gè)指針參數(shù)“this”,這個(gè)指針

指向該對(duì)象自己。該參數(shù)是隱含的不可見,在被調(diào)用的成員函數(shù)內(nèi),出現(xiàn)的所有本類成員前

都隱含的加上“this->”,也可以顯式使用this指針。

例:

classcomplex

private:

doublereal;

doubleimage;

public:

voidset(doubler,doublei)

(

this->real=r;

this->image=i;

)

voiddisplay()

(

cout?real?',+',?image?',i"?endl;

}

);

voidmain()

(

complexx,y;

x.set(l,3);

x.displayO;

y.set(3,5);

y.display();

)

注:“set”函數(shù)把this指針顯式寫出了,和原先效果-樣。this指針使得對(duì)象能夠訪問自己的

成員。

二、靜態(tài)成員

類的靜態(tài)成員屬于類本身,而不屬于具體的某個(gè)對(duì)象,所以一個(gè)類靜態(tài)成員只有一個(gè)數(shù)據(jù)拷

貝。

類的靜態(tài)數(shù)據(jù)成員是在類定義語句被執(zhí)行時(shí)即創(chuàng)建,所以是靜態(tài)生存期,與對(duì)象的創(chuàng)建和注

銷無關(guān)。

定義方法:在類成員定義中加關(guān)鍵字“static”,然后在類外為靜態(tài)數(shù)據(jù)成員分配內(nèi)存空間并初

始化。

調(diào)用方法:內(nèi)部,任何成員函數(shù)都可以直接調(diào)用;外部,用任何一個(gè)該類成員調(diào)用,或者用

類名加“直接調(diào)用。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

staticintsum;

voidset(doubler,doublei)

real=r;

image=i;

sum++;〃用靜態(tài)成員統(tǒng)計(jì)set函數(shù)的調(diào)用次數(shù)

)

voiddisplayO

(

cout?real?"+"?image?"i"?endl;

}

);

intcomplex::sum=0;

voidmain()

(

complexx,y;

x.set(l,3);

y.set(3,5);

cout?x.sum;

cout?complex::sum;

)

注:也可以定義靜態(tài)函數(shù)成員。靜態(tài)成員函數(shù)的特點(diǎn)是,不會(huì)自動(dòng)傳遞this指針,所以靜態(tài)

成員函數(shù)一般專用于處理靜態(tài)數(shù)據(jù)成員。

三、友元

在類中可以指定另?個(gè)類或別的函數(shù)為自己的友元。友元和類內(nèi)部成員函數(shù)的訪問權(quán)限相

同,即友元可以訪問類的所有成員。

例:

classcomplex

(

friendvoidset(doubler,doublei,complex*p);〃聲明了一個(gè)友元函數(shù)

friendclassc;〃聲明了類c是類complex的友元

friendvoidb::fl();〃聲明了類b的成員函數(shù)fl是友元函數(shù)

private:

doublereal;

doubleimage;

public:

voiddisplayO

(

cout?real?"+"?image?nin?endl;

)

);

voidset(doubler,doublei,complex*p)

(

p->real=r;

p->image=i;

voidmain()

(

complexx;

set(l,3,&x);

x.display();

)

注意:

1.友元函數(shù)并不是類的一個(gè)成員,而是單獨(dú)的一個(gè)外部函數(shù)。友元函數(shù)要訪問類的成員,

需要利用對(duì)象指針等參數(shù),指定要訪問的是哪個(gè)對(duì)象的成員。

2.如果指定類b是類a的友元類,則類b中的所有成員函數(shù)都可以任意訪問類a的成員。

但是反過來類a并不是類b的友元,不能任意訪問類b的成員。

四、常量成員和對(duì)象成員

類中的數(shù)據(jù)成員是常量或者另?個(gè)類的對(duì)象的情況。

例:

classB

(

ints;

);

classA

(

Bx;

constinty;

);

注意:

定義類的時(shí)候不能給常量成員指定初值,常量成員是屬于對(duì)象的,每個(gè)對(duì)象的同名常量成員

可以取不同的值。

五、構(gòu)造函數(shù)

1.因?yàn)閷?duì)象定義后在外部無法訪問私有的成員,所以用?個(gè)特殊的成員函數(shù),構(gòu)造函數(shù)給

對(duì)象初始化。當(dāng)系統(tǒng)新定義一個(gè)對(duì)象時(shí).,會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)。

構(gòu)造函數(shù)名字與類名相同,沒有返回值類型,可以重載,應(yīng)設(shè)置為公有權(quán)限。

如果沒有定義任何構(gòu)造函數(shù),系統(tǒng)會(huì)自動(dòng)產(chǎn)生一個(gè)默認(rèn)構(gòu)造函數(shù),這個(gè)默認(rèn)函數(shù)沒有參數(shù)并

且什么也不做。如果自定義了構(gòu)造函數(shù),系統(tǒng)就不會(huì)自動(dòng)產(chǎn)生默認(rèn)構(gòu)造函數(shù),注意這時(shí)不帶

參數(shù)定義對(duì)象可能就會(huì)出錯(cuò)。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

complex(doubler,doublei)〃構(gòu)造函數(shù)

(

real=r;

image=i;

)

voidset(doubler,doublei)

(

real=r;

image=i;

)

voiddisplay()

(

cout?real?',+',?image?',i"?endl;

)

);

voidmain()

(

complexx(l,3),y(3,4);〃通過構(gòu)造函數(shù)初始化

complexz;〃錯(cuò)誤,此時(shí)已經(jīng)沒有自動(dòng)生成的無參構(gòu)造函數(shù)了

x.displayO;

y.set(3,5);

y.displayO;

)

注:

構(gòu)造函數(shù)如果要求參數(shù),定義對(duì)象時(shí)必須用括號(hào)加上初始化參數(shù)。構(gòu)造函數(shù)不要求參數(shù),定

義時(shí)對(duì)象名后不加括號(hào)。構(gòu)造函數(shù)的參數(shù)可以帶上默認(rèn)值。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

complex(doubler=0,doublei=0)〃構(gòu)造函數(shù)

(

real=r;

image=i;

)

);

voidmain()

(

complexx;〃構(gòu)造函數(shù)默認(rèn)參數(shù),復(fù)數(shù)為0

complexy(3,4);

2.冒號(hào)初始化列表

C++允許構(gòu)造函數(shù)在冒號(hào)后加成員初始化列表,列表中用括號(hào)給出初始化參數(shù)。

初始化列表中列出的數(shù)據(jù)成員初始化的順序,按照類的聲明中這些成員出現(xiàn)的順序進(jìn)行,不

是按照初始化列表中出現(xiàn)的順序。一般初始化列表中的順序應(yīng)和聲明的順序一致。

類的對(duì)象成員,類的常量成員,對(duì)象繼承,引用型的參數(shù),這些情況下都必須使用初始化列

表。對(duì)于有構(gòu)造函數(shù)的數(shù)據(jù)成員,用初始化列表效率較高。

例:

classcomplex

private:

doublereal;

doubleimage;

public:

complex(doubler=0,doublei=O):real(r),image(i){}

);

voidmain()

(

complexx;

complexy(3,4);

例:

classcl

public:

cl(inti=0):a(i){}

inta;

);

classc2

public:

c2(inti=O):t(5),a(b+l),b(i){}

clt;

constintb;

inta;

);

voidmain()

(

c2x,y(3);

cout?x.a?y.a;

)

3.構(gòu)造函數(shù)中開辟動(dòng)態(tài)空間

在類中有一個(gè)指針成員,這個(gè)指針用來掛載動(dòng)態(tài)申請(qǐng)的存儲(chǔ)空間。

例:

classperson

(

private:

char*name;

intage;

public:

person(inta,char*p="nonamen);

voiddisplay()

(

cout?"name:n?name?uage:"?age?endl;

}

);

person::person(inta,char*p):age(a)

(

name=newchar[strlen(p)+l];

strcpy(name,p);

)

voidmain()

(

chart[20];

inta;

personx(18,"tom");

cout?"請(qǐng)輸入名字”;

cin?t;

coutvv"請(qǐng)輸入年齡”;

cin?a;

persony(a,t);

x.displayO;

y.display();

六、析構(gòu)函數(shù)

析構(gòu)函數(shù)用來在對(duì)象注銷之前執(zhí)行一些操作,特別是釋放動(dòng)態(tài)開辟的內(nèi)存空間等操作。

析構(gòu)函數(shù)名字是類名前加沒有返回值類型,一個(gè)類只有一個(gè)析構(gòu)函數(shù),不可以重載,

沒有參數(shù),應(yīng)設(shè)置為公有權(quán)限。

例:

classperson

private:

char*name;

iniage;

public:

person(inta,char*p=nnonamen):age(a)

(

name=newchar[strlen(p)+1];

strcpy(name,p);

)

~person()

(

delete[]name;

)

voiddisplay()

(

cout?uname:"?name?nage:u?age?endl;

七、轉(zhuǎn)換構(gòu)造函數(shù)

1.如果構(gòu)造函數(shù)可以只帶一個(gè)參數(shù),這個(gè)構(gòu)造函數(shù)就是隱式定義的類型轉(zhuǎn)換構(gòu)造函數(shù),可

以把這個(gè)參數(shù)的數(shù)據(jù)類型轉(zhuǎn)換為該類得類型。

定義過轉(zhuǎn)換構(gòu)造函數(shù)后,系統(tǒng)會(huì)在必要時(shí)自動(dòng)調(diào)用進(jìn)行隱式類型轉(zhuǎn)換,包括賦值、表達(dá)式計(jì)

算、參數(shù)傳遞等。

例:

重載復(fù)數(shù)類的構(gòu)造函數(shù)

classcomplex

(

private:

doublereal;

doubleimage;

public:

voidset(doubler,doublei)

(

real=r;

image=i;

)

voiddisplayO

(

cout?real?',+,,?iTnage?',i,,?endl;

complex(doubleredoublei)

real=r;

image=i;

)

complex(doublex):real(x),image(x){}〃轉(zhuǎn)換構(gòu)造函數(shù)

);

voidmain()

(

complexx(2,3);

complexy(4);

y=6;

x.displayO;

y.display();

}

2.顯示定義的轉(zhuǎn)換函數(shù),用operator加類型名,重載成成員函數(shù),無返回值類型??梢园?/p>

對(duì)象轉(zhuǎn)換成其他類型。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

voidset(doubleredoublei)

(

real=r;

image=i;

)

voiddisplayO

(

cout?real?"+',?image?,'i"?endl;

)

complex(doubler,doublei):real(r),image(i){)

operatordouble?!ㄞD(zhuǎn)換函數(shù)

(

doublet;

t=real+image;

returnt;

}

);

voidmain()

complexx(2,3);

doublea;

a=x;

cout?a?endl;

x.displayO;

八、拷貝構(gòu)造函數(shù)

1.根據(jù)現(xiàn)有對(duì)象,創(chuàng)建一個(gè)相同的新對(duì)象時(shí),需耍用到拷貝構(gòu)造函數(shù)。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

voidset(doubler,doublei)

(

real=r;

image=i;

}

voiddisplayO

(

cout?real?"+"?image?"i"?endl;

)

complex(doubler,doublei)

{

real=r;

image=i;

)

complex(constcomplex&t)〃拷貝構(gòu)造函數(shù),形參是一個(gè)只讀的引用

(

real=t.real;

image=t.image;

)

);

voidmain()

(

complexx(2,3);

complexy(x);〃使用拷貝構(gòu)造函數(shù)

y.display();

除了在定義對(duì)象時(shí)使用拷貝構(gòu)造函數(shù),當(dāng)函數(shù)參數(shù)是對(duì)象時(shí),實(shí)參對(duì)象傳給形參會(huì)調(diào)用拷貝

構(gòu)造函數(shù);當(dāng)函數(shù)返回值是一個(gè)對(duì)象時(shí),會(huì)利用拷貝構(gòu)造函數(shù)產(chǎn)生一個(gè)臨時(shí)對(duì)象表示函數(shù)返

回值。這也是為什么拷貝構(gòu)造函數(shù)的參數(shù)是對(duì)象的引用。

由于對(duì)象的創(chuàng)建和注銷都耗費(fèi)系統(tǒng)資源,一般函數(shù)參數(shù)和函數(shù)返回值都用對(duì)象的引用。

為了保證函數(shù)調(diào)用的成功,如果沒有自定義拷貝構(gòu)造函數(shù),系統(tǒng)會(huì)創(chuàng)建一個(gè)默認(rèn)拷貝構(gòu)造函

數(shù),它的功能是復(fù)制參數(shù)對(duì)象的所有數(shù)據(jù)成員到新建對(duì)象。

2.深拷貝和淺拷貝

當(dāng)對(duì)象中含有指針和動(dòng)態(tài)開辟的內(nèi)存空間時(shí),可以看到簡單的數(shù)據(jù)復(fù)制不能正確的拷貝對(duì)

象。

例:

classperson

(

private:

char*name;

intage;

public:

person(inta,char*p=,'nonameM);

person(constperson&t);

voiddisplayO

(

cout?,'name:"?name?,'age:"?age?endl;

}

voidsetname(char*p)

(

delete[]name;

name=newchar[strlen(p)+l];

strcpy(name,p);

)

~person()

(

delete[]name;

)

);

person::person(inta,char*p):age(a)

(

name=newcharfstrlen(p)+l];

strcpy(name,p);

}

person::person(constperson&t):age(t.age)

(

name=newchar[strlen()+1];〃給新對(duì)象開辟內(nèi)存空間

strcpy(name,);

)

voidmain()

personx(18,ntomn);

persony(x);

y.setname(nmikeM);

x.displayO;

y.display();

)

簡單的數(shù)據(jù)復(fù)制稱為淺拷貝,帶有動(dòng)態(tài)申請(qǐng)空間和指針的數(shù)據(jù)復(fù)制稱為深拷貝。系統(tǒng)自動(dòng)生

成的拷貝構(gòu)造函數(shù)只能實(shí)現(xiàn)淺拷貝。

九、成員函數(shù)重載

例:person類重載set函數(shù)

classperson

(

private:

char*name;

intage;

public:

person(inta,char*p=Hnoname");

person(constperson&t);

~person()

(

deletedname;

)

voiddisplay()

(

cout?"name:n?name?uage:"?age?endl;

)

voidset(char*p)〃參數(shù)是字符串,改名字

(

delete[]name;

name=newchar[strlen(p)+l];

strcpy(name,p);

)

voidset(inta)〃參數(shù)是數(shù)字,改年齡

{

age=a;

}

voidset(inta,char*p)〃改名字年齡

{

set(p);

set(a);

)

);

person::person(inta,char*p):age(a)

(

name=newchar[strlen(p)+l];

strcpy(name,p);

)

person::person(constperson&t):age(t.age)

(

name=newchar[strlen()+1];

strcpy(name,);

)

voidmain()

(

personx(18,utom");

persony(x);

y.set("m汰e");

x.set(20/abcn);

x.display();

y.displayO;

)

十、運(yùn)算符重載

C++中的運(yùn)算符能夠以函數(shù)重載的方式重新定義,以擴(kuò)充運(yùn)算符的功能。運(yùn)算符的函數(shù)形式

是運(yùn)算符前加“operator、運(yùn)算符可以被重載為類的成員函數(shù)或者友元函數(shù)。

例:復(fù)數(shù)類加法

classcomplex

(

private:

doublereal;

doubleimage;

public:

voiddisplay()

(

cout?real?',+,,?image?"i,'?endl;

)

complex(doubler=0,doublei=O):real(r),image(i){}

complexoperator+(complex&a)〃重載加法運(yùn)算

(

complext;

t.real=real+a.real;

t.image=image+a.image;

returnt;

voidmain()

complexx(2,3),y(4,5),z;

z=x+y;

z.displayO;

)

因?yàn)榘鸭臃ㄟ\(yùn)算定義為類的成員函數(shù),所以只帶了?個(gè)參數(shù)表示兩個(gè)加數(shù)中的一個(gè),另個(gè)

加數(shù)是調(diào)用這個(gè)成員函數(shù)的對(duì)象。參數(shù)用引用省去了創(chuàng)建和注銷對(duì)象的過程。

例:重載成友元

classcomplex

(

friendcomplexoperator+(complex&a,complex&b);

private:

doublereal;

doubleimage;

public:

voiddisplayO

(

cout?real?"+',?image?,'i,,?endl;

)

complex(doubler=0,doublei=O):real(r),image(i){}

);

complexoperator+(complex&a,complex&b)〃重載力口法運(yùn)算

(

complext;

t.real=a.real+b.real;

t.image=a.image+b.image;

returnt;

)

voidmain()

(

complexx(2,3),y(4,5),z;

z=x+y;

z.display();

)

運(yùn)算符重載有一定限制,不是所有運(yùn)算法都可以重載成友元,也不是所有運(yùn)算符都可以重載,

運(yùn)算符重載也不能改變結(jié)合性和優(yōu)先級(jí),參數(shù)個(gè)數(shù)等。

十一、使用構(gòu)造函數(shù)的其他格式

1.當(dāng)構(gòu)造函數(shù)的參數(shù)只有一個(gè)時(shí).,用“?!焙陀?=”等效,調(diào)用的都是構(gòu)造函數(shù)。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

voiddisplay()

(

cout?real?"+"?image?"i"?endl;

)

complex(doubler)

(

real=r;

image=r;

)

complex(constcomplex&t)

{

real=t.real;

image=t.image;

}

);

voidmain()

(

complexx=2;

complexy=x;

y.display();

)

2.構(gòu)造函數(shù)也可以直接調(diào)用,會(huì)產(chǎn)生一個(gè)臨時(shí)對(duì)象作為返回值。

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

voiddisplay()

(

cout?real?"+',?image?,'i,'?endl;

)

complex(doubler,doublei)

(

real=r;

image=i;

)

complex(constcomplex&t)

real=t.real;

image=t.image;

voidmain()

(

complexx(l,2);

x=complex(4,5);//除了調(diào)用構(gòu)造函數(shù),還調(diào)用了賦值運(yùn)算符

x.displayO;

)

例:

classcomplex

(

private:

doublereal;

doubleimage;

public:

voiddisplay()

(

cout?real?"+',?image?,'i,,?endl;

)

complex(doubler=0,doublei=O):real(r),image(i){}

complexoperator+(complex&a)〃重載加法運(yùn)算

(

returncomplex(real+a.real,image+a.image);

)

);

voidmain()

(

complexx(2,3),y(4,5),z;

z=x+y;

z.displayO;

I

十二、典型運(yùn)算符重載

1.賦值運(yùn)算符

賦值運(yùn)算只能被重載為成員函數(shù)。賦值運(yùn)算符重載不能被繼承。如果不定義賦值運(yùn)算符重載,

系統(tǒng)在需要時(shí)會(huì)自動(dòng)生成一個(gè),效果是實(shí)現(xiàn)對(duì)象數(shù)據(jù)成員的淺拷貝。

例:

classperson

(

private:

char*name;

intage;

public:

person(inta=20,char*p="noname");

voiddisplayO

{

cout?uname:n?name?uage:u?age?endl;

)

-person()

(

delete[]name;

)

person&operator=(person&t)

{

if(this==&t)return*this;

age=t.age;

if(name)

(

delete[]name;

name=NULL;

}

if()

(

name=newchar[strlen()+1];

strcpy(name,);

)

return*this;

)

);

person::person(inta,char*p):age(a)

(

name=newchar[strlen(p)+l];

strcpy(name,p);

)

voidmain()

(

personx(18,"tom");

persony;

y=x;

x.displayO;

y.display();

)

注意:賦值運(yùn)算符和拷貝構(gòu)造函數(shù)是兩個(gè)不同的函數(shù),賦值運(yùn)算符一定要考慮賦值號(hào)兩邊是

同一個(gè)對(duì)象的情況,還要清理左邊的對(duì)象。賦值運(yùn)算符的返回值是一個(gè)對(duì)象引用,即被賦值

的對(duì)象本身。

2.自增運(yùn)算符重載

因?yàn)樽栽鲞\(yùn)算分前綴后綴兩種情況,重載時(shí),后綴運(yùn)算視為雙目運(yùn)算,第二個(gè)參數(shù)是int型

的0?

前綴++的返回值是左值,后綴++的返回值是右值。

例:復(fù)數(shù)類

classcomplex

(

private:

doublereal;

doubleimage;

public:

voidset(doubleredoublei)

(

real=r;

image=i;

)

voiddisplayO

(

cout?real?"+',?image?,'i,,?endl;

)

complex(doubler=0,doublei=O):real(r),image(i){}

complex&operator++()〃前綴++

(

real++;

image++;

return%his;

)

complexoperator++(int)〃后綴++

(

complext=*this;〃用到了拷貝構(gòu)造函數(shù)

real++;

image++;

returnt;

)

);

voidmain()

(

complexx(2,3),y(l,l);

x=y++;

x.display();

y.display();

++y;

y.displayO;

例:重載為友元函數(shù)

classcomplex

(

friendcomplex&operator++(complex&a);

friendcomplexoperator++(complex&a,int);

private:

doublereal;

doubleimage;

public:

voidset(doubleredoublei)

(

real=r;

image=i;

)

voiddisplayO

(

cout?real?',+',?image?,'i"?endl;

)

complex(doubler=0,doublei=O):real(r),image(i){}

);

complex&operator++(complex&a)〃前綴++

(

a.real++;

a.image++;

returna;

)

complexoperator++(complex&a,int)〃后綴++

(

complext=a;

a.real++;

a.image++;

returnt;

I

voidmain()

(

complexx(2,3),y(l,l);

x=y++;

x.display();

y.display();

++y;

y.display();

}

重載為友元就多了一個(gè)參數(shù)。

第八章繼承與派生

8.1繼承概念

一、繼承概念和派生類定義

繼承就是在已有的類基礎(chǔ)匕添加新成員,構(gòu)成一個(gè)新的類,新類中的成員包括新加成員和

繼承過來的成員。

被繼承的類稱為基類、父類,新類稱為派生類、子類。

定義派生類時(shí),用冒號(hào)表示基類,用public、private、protected表示公有、私有、保護(hù)三種

繼承方式。不寫繼承方式時(shí),用class定義的派生類默認(rèn)私有繼承,用struct定義的派生類

默認(rèn)公有繼承。

例:

#include<iostream>

usingnamespacestd;

classcircle

(

public:

doubler;

doublearea()

{

returnr*r*3.14159;

}

voidset(doublea)

{

r=a;

}

);

classcylinder:publiccircle

(

public:

doubleh;

doublevolume()

(

returnr*r*3.14159*h;

)

voidsetc(doublea,doubleb)

(

r=a;

h=b;

)

);

voidmain()

circlex;

x.set(3);

cout?x.area()?endl;

cylindery;

y.setc(3,5);

cout?y.volume()?endl;

cout?y.area()?endl;

圓柱類公有繼承自圓類,圓柱類擁有圓類的所有成員,外加自己新定義的成員。

二、類成員的訪問屬性

1.類成員訪問屬性分為四種

定義類成員時(shí),publicprotectedprivate表示公有、保護(hù)、私有三種訪問權(quán)限。

類內(nèi)成員函數(shù)可以訪問本類的私有、保護(hù)、公有成員,類外函數(shù)通過對(duì)象只能訪問公有成員。

派生類新加成員函數(shù)不可以訪問原基類的私有成員,但是從基類繼承的成員函數(shù)可以。

類成員訪問屬性分為四種:公有、保護(hù)、私有、隱藏(基類私有成員)。

2.繼承方式對(duì)成員訪問屬性的影響

不論采用何種繼承方式,基類私有成員到了派生類中一定變成了隱藏屬性,以下討論基類的

公有和保護(hù)成員。

公有繼承:基類成員的訪問屬性不變。

保護(hù)繼承:基類成員訪問屬性全部變成保護(hù)。

私有繼承:基類成員訪問屬性全部變成私有。

注意,派生類中的隱藏成員并不是不存在,隱藏成員是通過基類的公有成員函數(shù)這個(gè)接口來

訪問的。

#include<iostream>

usingnamespacestd;

classM

private:

intc;

protected:

intb;

public:

inta;

voidset(intx,inty,intz)

b=y;

voiddisplayO

cout?a?',?b?',?c?endl;

classN:publicM

(

public:

intd;

);

voidmain()

(

Nx;

x.set(l,2,3);

x.d=4;

x.displayO;

cout?x.a?x.d?endl;

)

建議:;在設(shè)計(jì)類時(shí)盡量用私有成員,私有成員對(duì)派生類是隱藏的,派生類中只考慮基類的

接口函數(shù)即可;繼承方式盡量用公有,這樣不會(huì)改變基類成員的訪問屬性。

三、多繼承和多重繼承

如果派生類從多個(gè)基類繼承而來,叫做多繼承。

如果類A派生出類B,類B又派生出類C,叫做多重繼承。

繼承必須單向,不允許構(gòu)成環(huán)形結(jié)構(gòu)。

四、基類成員二義性處理

如果派生類新定義的成員,和從基類繼承過來的可訪問成員名字重復(fù),這時(shí)默認(rèn)訪問派生類

新定義的成員。如果是多重繼承,幾個(gè)基類中有重名成員,優(yōu)先訪問繼承樹中距離近的成員。

如果要訪問指定的某個(gè)基類成員,在成員名前加上定義它的基類名和"::“。

在派生類中定義基類的同名成員,不會(huì)形成函數(shù)重載,而是把基類成員重新定義和修正,使

之適應(yīng)派生出來的新類。

重載是overload,意為超載。重定義是覆蓋override,重定義的成員一般要求參數(shù)表與老版

本保持一致。

例:

#include<iostream>

usingnamespacestd;

classcircle

(

private:

doubler;

public:

doublearea()

returnr*r*3.14159;

doublecircumference()

(

return2*r*3.14159;

)

voidset(doublea)

(

r=a;

)

);

classcylinder:publiccircle

(

private:

doubleh;

public:

doublevolume()

(

returncircle::area()*h;

)

doublearea()

(

returncircle::area()*2+circumference()*h;

)

voidset(doublea,doubleb)

(

circle::set(a);

h=b;

)

);

voidmain()

(

circlex;

x.set(3);

cout?x.area()?endl;

cylindery;

y.set(3,5);

cout?y.volume()?endl;

cout?y.area()?endl;

cout?y.circle::area()?endl;

}

例:

#include<iostream>

usingnamespacestd;

structA"struct定義的類默認(rèn)公有成員,默認(rèn)公有繼承

{inta;};

structB:A

{intb;};

structC:B

{inta;};

voidmain()

(

Ct;

t.A::a=l;

cout?t.B::a;

t.B::a=5;

cout?t.A::a;

)

運(yùn)算結(jié)果:1

5

注意:只要不產(chǎn)生二義性,這里用類A的派生類類B調(diào)用成員a也可以。調(diào)用被覆蓋了的

基類成員,最好用該成員第一次定義所在類的類名,即用A來調(diào)用。

五、特殊成員的繼承特性

1.靜態(tài)成員

基類的靜態(tài)成員可以被繼承,基類和派生類將共享同一個(gè)靜態(tài)成員。

2.友元

基類的友元并不會(huì)成為派生類的友元,但是基類成員如果是別的類的友元,派生后仍保留友

元權(quán)限。

六、派生類的構(gòu)造和析構(gòu)

構(gòu)造函數(shù)和析構(gòu)函數(shù)都不能繼承。

創(chuàng)建對(duì)象時(shí),通過初始化列表,派生類調(diào)用基類的構(gòu)造函數(shù)完成初始化。

初始化列表只是初始化參數(shù)的分配表,它的順序和實(shí)際初始化的順序無關(guān)。系統(tǒng)先自動(dòng)調(diào)用

基類構(gòu)造函數(shù)初始化基類,再初始化派生類的新成員。

如果初始化列表中沒有基類構(gòu)造函數(shù),系統(tǒng)仍然會(huì)自動(dòng)調(diào)用基類構(gòu)造函數(shù),但是不給參數(shù)。

注銷對(duì)象時(shí),系統(tǒng)在執(zhí)行完派生類析構(gòu)函數(shù)后自動(dòng)調(diào)用基類的析構(gòu)函數(shù),基類析構(gòu)函數(shù)名不

出現(xiàn)。先注銷派生類新加成員,再調(diào)用基類析構(gòu)函數(shù)注銷基類成員。

例:

classA

(

public:

inta;

A(inti):a(i)

cout?"A"?'

~A()

(

cout?"~A"?'

}

);

classB:publicA

(

public:

inib;

B(inti,intj):b(j),A(i)

{

cout?"B"?'

}

~B()

{

cout?"~B"?'

}

);

classC:publicB

(

public:

intc;

C(inti,intj,intk):c(k),B(i,j)

{

cout?"C"?'

}

~C()

{

cout?"~C"?'

)

);

voidf()

(

Cx(2,3,4);

cout?x.a?x.b?x.c?endl;

)

voidmain()

f();

多重繼承情況下,系統(tǒng)只是調(diào)用自己上一層的父類的構(gòu)造和析構(gòu)函數(shù),并不會(huì)調(diào)用祖父類的。

例:

classA

(

public:

inta;

A(inli):a(i)

(

cout?"A"?'

)

~A()

{

cout?"-A"?'

)

};

classB

(

public:

intb;

B(inti):b(i)

{

cout?"B"?'

}

~B()

{

cout?"~B"?'

classC:publicB,publicA

(

public:

intc;

C(inti,intj,intk):B⑴,A①,c(k)

{

cout?"C"?'

)

~C()

(

cout?"~C"?'

}

};

voidf()

cx(2,3,4);

cout?x.a?x.b?x.c?endl;

)

voidmain()

(

f();

)

七、虛基類和虛繼承

1.在復(fù)雜的樹形繼承關(guān)系中,有時(shí)會(huì)形成數(shù)據(jù)冗余。

例:

classA

{inta;};

classB:A

{intb;};

classC:A,B

{intc;};

這種情況下,明顯有兩份A類拷貝,數(shù)據(jù)冗余。這種繼承方式大部分C++編譯器會(huì)報(bào)錯(cuò)。

例:

classA

{inta;};

classBI:A

{intx;};

classB2:A

{inty;};

classC:B,C

{intc;};

此時(shí)D類的對(duì)象中有兩份A類拷貝。這時(shí)系統(tǒng)不會(huì)報(bào)錯(cuò),可以通過Bl::a和B2::a分別調(diào)用

兩個(gè)成員a。注意不能用A::a。

例:

classA

(

public:

inta;

A(inti):a(i){)

);

classBkpublicA

(

public:

intx;

Bl(inti,intj):A(i),x(j){)

);

classB2:publicA

public:

inty;

B2(inti,intj):A(i),y(j){)

);

classC:publicBl,publicB2

(

public:

intc;

C(inti,intj,intk,intm,intn):B1(ij),B2(m,n),c(k){}

);

voidmain()

(

Ct(l,2,3,4,5);

cout?t.B1::a?t.B2::a?t.x?t.y?t.c;

)

2.虛繼承

虛繼承專門解決上述繼承方式下(菱形繼承)的數(shù)據(jù)冗余,虛繼承將重復(fù)的基類拷貝合并成

一個(gè)。

加了virtual關(guān)鍵字的繼承方式叫做虛繼承。

例:

classA

{inta;};

classB1:virtualpublicA

{intx;};

classB2:virtualpublicA

{inty;};

classC:B1,B2

{intb;};

此時(shí)類Bl和B2虛繼承了類A,類A是類Bl和類B2的虛基類,或說Bl和B2得到的類A

拷貝是一個(gè)虛的基類拷貝。并不是類A就是虛基類,如果一個(gè)類X以一般方式繼承了類A,

類A就是類X的“實(shí)”基類。

虛的基類拷貝在多重繼承樹中向下傳遞(以一般的繼承方式),不管傳遞多少代,仍然是虛

的。類C從B1和B2得到的類A拷貝就仍然是一個(gè)虛的類A拷貝。

從不同父類多次繼承的同一虛基類,只會(huì)產(chǎn)生一份類拷貝。此時(shí)父類對(duì)虛基類的初始化失效

(因?yàn)槎鄠€(gè)父類中包含多次初始化,但是類拷貝只有一個(gè)),派生類直接調(diào)用虛基類的構(gòu)造

函數(shù)進(jìn)行初始化。

例:

classA

(

public:

inta;

A(inti):a(i){)

classB1:virtualpublicA

public:

intx;

Bl(inti,intj):A(i),x(j){)

);

classB2:publicvirtualA

(

public:

inty;

B2(inti,intj):A(i),yG){}

);

classC:publicB1,publicB2

(

public:

intc;

C(inti,intj,intm,intn):A(i),B1(7,j),B2(8,m),c(n){}

);

voidmain()

(

Ct(l,2,3,4);

cout?t.a?t.x?t.y?t.c;

8.2多態(tài)

一、派生類和基類的關(guān)系

i.派生類是由基類添加新成員而產(chǎn)生的基類的進(jìn)一步細(xì)分?;愂强傤愋?,派生類是子類

型,所以派生類對(duì)象也是屬于基類這個(gè)總類型的。

從多個(gè)基類繼承而來的派生類屬于交叉類型,可以認(rèn)為是基類的組合。多繼承的派生類是它

的任何一個(gè)基類的子類型。

例:

基類humen類,派生類student類、teacher類,顯然任何一個(gè)student和teacher也應(yīng)該是humen

類的。

基類“自然科學(xué)”,派生類“物理”、“化學(xué)”,“物理”“化學(xué)”派生出“物理化學(xué)物理化學(xué)是交

叉學(xué)科,既是物理學(xué)的分支,又是化學(xué)的分支。

2.派生類對(duì)象也屬于基類這個(gè)總類型,所以派生類對(duì)象總是可以當(dāng)做基類對(duì)象,把派生類

對(duì)象寫在基類對(duì)象的位置不會(huì)報(bào)告類型不匹配錯(cuò)誤。此時(shí)忽略派生類新加成員,只考慮繼承

來的基類成員。

把派生類對(duì)象直接當(dāng)做基類對(duì)象來用,類似于把整數(shù)當(dāng)做實(shí)數(shù)來用。

a.派生類的地址可以賦值給基類指針,派生類對(duì)象可以用來初始化基類引用。此時(shí)通過基

類指針和基類引用,訪問的是派生類對(duì)象中從基類繼承過去的那部分成員。

b.用基類對(duì)象、基類指針、基類引用作參數(shù)的函數(shù),也可以用派生類的相應(yīng)參數(shù)。即處理

基類對(duì)象的函數(shù)也可以處理派生類對(duì)象中繼承自基類的部分成員。

c.派生類對(duì)象可以賦值給基類對(duì)象,調(diào)用基類的賦值運(yùn)算符重我。派生類對(duì)象可以用來初

始化基類對(duì)象,調(diào)用基類的拷貝構(gòu)造函數(shù)。

例:

classperson

(

private:

charname[20];

intage;

public:

voiddisplayO

(

cout?Hname:H?name?",'?',age:,,?age?endl;

)

intgetage()

(

returnage;

)

person(char*p,inta):age(a)

{

strcpy(name,p);

)

person&operator=(person&t)

(

if(this==*t)return*this;

age=t.age;

strcpy(name,p);

return*this;

)

);

classstudent:publicperson

(

private:

intscore;

public:

voiddisplayO

(

cout?"score:,'?score<<Hu;

person::display();

student(char*p,inta,intb):person(p,a),score(b){}

);

voidf(person&t)

(

inta=t.getage();

if(a<30)

cout?Hyoungboy”;

else

cout?"aman

t.displayO;

)

voidmain()

(

personx(utom",32);

studenty(nmike",18,80);

f(x);

f(y);

x.displayO;

y.display();

x=y;

x.displayO;

)

例:派生類的賦值運(yùn)算符

classperson

(

private:

charname[20J;

intage;

public:

voiddisplayO

(

cout?"name:',?name?H,,?"age:"?age?endl;

)

intgetage()

(

returnage;

)

person(char*p,inta):age(a)

(

strcpy(name,p);

person&operator=(person&t)

if(this==&t)return*this;

age=t.age;

strcpy(name,);

return*this;

);

classstudent:publicperson

(

private:

intscore;

public:

voiddisplayO

(

cout?nscore:n?score?n

person::display();

}

student(char*p,inta,intb):person(p,a),score(b){}

student&operator=(student&t)

if(this==&t)retum*this;

operator=(t);

score=t.score;

return*this;

或者定義為

student&operator=(student&t)

if(this==&t)retum九his;

static_cast<person&>(*this)=t;

*(static_cast<person*>(this))=t;

score=t.score;

return*this;

)

static_cast是c++U」的強(qiáng)制類型轉(zhuǎn)換格式。

3.對(duì)于針對(duì)基類的代碼來說,一個(gè)對(duì)象不管是基類對(duì)象還是派生類對(duì)象,都是用同樣方法

處理的,派生類是基類定義的延續(xù),或者說詳細(xì)定義過的基類。

處理基類的函數(shù)也可以處理派生類,但是這時(shí)派生類的新成員不能使用了。虛函數(shù)和動(dòng)態(tài)多

態(tài)正是為了解決這個(gè)問題。

1.多態(tài)概念

多態(tài)指不同的對(duì)象對(duì)同一消息會(huì)作出不同的反應(yīng)。程序設(shè)計(jì)中的消息指的就是函數(shù)調(diào)用,多

態(tài)指的是不同對(duì)象調(diào)用同一個(gè)函數(shù)名,實(shí)際執(zhí)行了不同的代碼。

多態(tài)分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài),又叫靜態(tài)聯(lián)編和動(dòng)態(tài)聯(lián)編。靜態(tài)多態(tài)通過函數(shù)重載實(shí)現(xiàn),動(dòng)

態(tài)多態(tài)由虛函數(shù)實(shí)現(xiàn)。

2.虛函數(shù)

虛函數(shù)是用virtual關(guān)鍵字修飾的類的非私有成員函數(shù)。私有成員是基類專用派生類隱藏的,

不能定義成虛函數(shù)。

虛函數(shù)在繼承樹中派生下去仍然是虛函數(shù),在派生類中重新定義虛函數(shù)時(shí)不管加不加

virtual,重新定義的同名函數(shù)也仍然是虛函數(shù)。

虛函數(shù)的重新定義要求函數(shù)參數(shù)表和返回值都保持不變,否則即使加了virtual也不會(huì)是虛

函數(shù)。

虛函數(shù)不能內(nèi)聯(lián),即使把虛函數(shù)定義寫在類內(nèi),它也不會(huì)是內(nèi)聯(lián)函數(shù)。

當(dāng)把一個(gè)基類指針指向派生類對(duì)象時(shí),或者把一個(gè)基類引用綁定一個(gè)派生類對(duì)象時(shí),利用基

類指針或基類引用調(diào)用虛成員函數(shù),調(diào)用的不是基類中定義的老版本,而是派生類中重新定

義的最新版本。

例:

classperson

(

private:

charname[20];

intage;

public:

virtualvoiddisplayO

(

cout?nname:"vvname<<""vv"age:"vvagevvendl;

)

intgetage()

(

returnage;

)

person(char*p,inta):age(a)

(

strcpy(name,p);

)

);

classstudent:publicperson

(

private:

intscore;

public:

voiddisplayO

cout?nscore:n?score?Mu;

person::display();

)

student(char*p,inta,intb):person(p,a),score(b){}

)

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論