C++多態特性之派生與虛函數與模板詳細介紹_第1頁
C++多態特性之派生與虛函數與模板詳細介紹_第2頁
C++多態特性之派生與虛函數與模板詳細介紹_第3頁
C++多態特性之派生與虛函數與模板詳細介紹_第4頁
C++多態特性之派生與虛函數與模板詳細介紹_第5頁
已閱讀5頁,還剩15頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第C++多態特性之派生與虛函數與模板詳細介紹目錄繼承與派生虛函數父類代碼如下模板函數模板類模板字符串

繼承與派生

C++是面向對象編程,那么只要面向對象,都會有多態、繼承的特性。C++是如何實現繼承的呢?

繼承(Inheritance)可以理解為一個類從另一個類獲取成員變量和成員函數的過程。例如類B繼承于類A,那么B就擁有A的成員變量和成員函數。

在C++中,派生(Derive)和繼承是一個概念,只是站的角度不同。繼承是兒子接收父親的產業,派生是父親把產業傳承給兒子。

被繼承的類稱為父類或基類,繼承的類稱為子類或派生類。子類和父類通常放在一起稱呼,基類和派生類通常放在一起稱呼。

在C++中繼承稱為派生類,基類孵化除了派生類,使用:來表示子類繼承父類,C++中支持多繼承,使用逗號分隔

classParent{

public:

intname;

protected:

intcode;

private:

intnum;

classParent1{

//C++中,:表示繼承,可以多繼承逗號分隔

//public/protected/private繼承,對于基類起到一些保護機制默認是private繼承

classChild:publicParent,Parent1{

voidtest(){

//派生類可以訪問到public屬性和protected屬性

this-name;

this-code;

};

C++中派生類中添加了public派生、protected派生、private派生,默認是private派生

class派生類名:[繼承方式]基類名{派生類新增加的成員};

classParent{

public:

intname;

protected:

intcode;

private:

intnum;

classParent1{

//private私有繼承

classChild1:privateParent{

voidtest(){

this-name;

this-code;

//protected繼承

classChild2:protectedParent{

voidtest(){

this-name;

this-code;

};

public派生、protected派生、private派生對于,創建的對象調用父類的屬性和方法起到了限制和保護的作用

Childchild;

;//public繼承。調用者可以訪問到父類公有屬性,私有屬性訪問不到的

Child1child1;

//;//private繼承.調用者訪問不到父類公有屬性和私有屬性

Child2child2;

//;//protected繼承,調用者訪問不到父類公有屬性和私有屬性

虛函數

重點?。?!C++的繼承和java中的繼承存在的不同點:基類成員函數和派生類成員函數不構成重載

基類成員和派生類成員的名字一樣時會造成遮蔽,這句話對于成員變量很好理解,對于成員函數要引起注意,不管函數的參數如何,只要名字一樣就會造成遮蔽。換句話說,基類成員函數和派生類成員函數不會構成重載,如果派生類有同名函數,那么就會遮蔽基類中的所有同名函數,不管它們的參數是否一樣。?

父類代碼如下

#includecstring

#includeiostream

usingnamespacestd;

classPerson{

protected:

char*str;

public:

Person(char*str){

if(str!=NULL){

this-str=newchar[strlen(str)+1];

strcpy(this-str,str);

}else{

this-str=NULL;

cout"parent"endl;

Person(constPersonp){

cout"copyparent"endl;

voidprintC(){

cout"parentprintC"endl;

~Person(){

//if(str!=NULL){

//delete[]str;//如果調用了這個方法只會調用一次析構函數

//cout"parentdestroy"endl;

};

子類繼承父類,并且調用父類的構造函數,通過:來調用父類的構造函數

//子類

classCTest:publicPerson{

public:

//調用父類的構造方法

CTest(char*str):Person(str){

cout"child"endl;

voidprintC(){

cout"childprintC"endl;

~CTest(){

cout"childdestroy"endl;

};

在C++中和Java的不同在于如下代碼:只要是父類的指針都是調用的父類的方法,哪怕子類對象直接賦值給父類,也會調用父類的方法,而不會調用子類的方法。

intmain(){

Personperson=CTest("jake");

person.printC();//parentprintC

cout"-----------"endl;

Person*p=NULL;

CTestc1("123");

p=

c1.printC();//childprintC

p-printC();//parentprintC為什么會調用的是parent的方法呢?

return0;

}

parent

child

copyparent

childdestroy

parentprintC

-----------

parent

child

childprintC

parentprintC

childdestroy

哪怕通過指針傳遞和引用傳遞,只要使用的父類都會調用父類的方法

//通過指針傳遞只會調用父類的方法,不會調用子類的方法

voidhowToPaint(Person*p){

p-printC();

//通過引用類型,只會調用父類的方法,不會調用子類的方法

voidhowToPaint1(Personp){

p.printC();

}

cout"---------"endl;

howToPaint(p);//parentprintC

howToPaint(c1);//parentprintC

cout"-------"endl;

Personp1("123");

//都是父類的方法

howToPaint1(p1);//parentprintC

howToPaint1(c1);//parentprintC

cout"--------"endl;

CTestc2("123");

Personp2=c2;//會不會調用父類的拷貝函數呢?copyparent會進行調用

---------

parentprintC

parentprintC

-------

parent

parentprintC

parentprintC

--------

parent

child

copyparent

childdestroy

childdestroy

這是為什么呢?

C++中會按照函數表的順序進行調用,很顯然父類的函數是在子類函數的前面的

那么如何調用到子類的方法呢?C++提供了虛函數的方式,虛函數也是實現多態的關鍵。?

虛函數與純虛函數,純虛函數在java中abstract==純虛函數

實際開發中,一旦我們自己定義了析構函數,就是希望在對象銷毀時用它來進行清理工作,比如釋放內存、關閉文件等,如果這個類又是一個基類,那么我們就必須將該析構函數聲明為虛函數,否則就有內存泄露的風險。也就是說,大部分情況下都應該將基類的析構函數聲明為虛函數。

包含純虛函數的類稱為抽象類(AbstractClass)。之所以說它抽象,是因為它無法實例化,也就是無法創建對象。原因很明顯,純虛函數沒有函數體,不是完整的函數,無法調用,也無法為其分配內存空間。

抽象類通常是作為基類,讓派生類去實現純虛函數。派生類必須實現純虛函數才能被實例化。

一個純虛函數就可以使類成為抽象基類,但是抽象基類中除了包含純虛函數外,還可以包含其它的成員函數(虛函數或普通函數)和成員變量。只有類中的虛函數才能被聲明為純虛函數,普通成員函數和頂層函數均不能聲明為純虛函數?;惖奈鰳嫼瘮当仨毬暶鳛樘摵瘮?。

#includeiostream

usingnamespacestd;

classPerson{

public:

//增加了一個虛函數表的指針

//虛函數子類可以覆寫的函數

virtualvoidlook(){

cout"virtuallook"endl;

//純虛函數必須要讓子類實現的

virtualvoidspeak(){};

//基類的析構函數必須聲明為虛函數

virtual~Person(){

cout"~Person"endl;

classChild:publicPerson{

public:

//子類實現純虛函數

voidspeak()override{

cout"childspeak"endl;

//訪問父類的方法

voidlook()override{

cout"childlook"endl;

Person::look();

~Child(){

cout"~Child"endl;

intmain(){

Person*person=newChild();//必須通過指針的方式,不同通過棧的方式去派生抽象

person-speak();//childspeak

person-look();//childlook

Personp;

coutsizeof(p)endl;//8這就表明了虛函數是有一個虛函數表,增加一個指針*vtable,指向了虛函數表

//下面代碼來證明

typedefvoid(*func)(void);

funcfun=NULL;

cout(int*)pendl;//指向函數的首地址0x16ee1efa8

cout(int*)*(int*)pendl;//函數的地址0xfe40a0

fun=(func)*((int*)*(int*)

fun();//virtuallook

return0;

*childspeak

*childlook

*virtuallook

*~Child

*~Person

*/

childspeak

childlook

virtuallook

8

0x16ee1efa8

0xfe40a0

模板

模板和java的泛型類似。模板類不支持聲明(.h)和實現(.cpp)分開寫,「不能將模板的聲明和定義分散到多個文件中」的根本原因是:模板的實例化是由編譯器完成的,而不是由鏈接器完成的,這可能會導致在鏈接期間找不到對應的實例。

函數模板

#includeiostream

#includestring

#includecstring

usingnamespacestd;

*函數模板和java中的泛型類似

//方法泛型這里只能聲明在方法上

templatetypenameT,typenameR=int

//R的默認類型是int

//typename==class兩個等價的

voidswap2(Tt,Rr){

templatetypenameT

voidswapT(Ta,Tb){

cout"swap:TaTb"endl;

Ttemp=a;

a=b;

b=temp;

//普通函數優先級比泛型函數高,只有類型重合的狀態下

voidswapT(inta,intb){

cout"swap:intaintb"endl;

inttemp=a;

a=b;

b=temp;

intmain(){

//函數模板

inta=10;

intb=20;

charc='a';

swapTint(a,b);//顯示調度

swapT(a,b);//自動推導

//swap(a,c);//報錯無法推導出具體的類型

//swap2();//報錯無法推導出具體的類型

char*a1="abc";

char*a2="123";

couta1a2endl;

swapT(a1,a2);

couta1a2endl;

return0;

}

swap:TaTb

swap:intaintb

abc123

swap:TaTb

123abc

類模板

#includeiostream

#includecstring

usingnamespacestd;

//模板修飾在類上

templatetypenameT,typenameR

classPerson{

public:

Ta;

Rb;

Person(Tt){

TgetA(){

Tt1;

//returnt1;//這里不可以返回,因為方法執行完畢后會銷毀掉

returna;//返回值是引用

*和java不同的部分,比java更加靈活

classPp{

public:

voidshow(){

cout"Ppshow"endl;

templatetypenameT

classObjTemp{

private:

Tobj;

public:

voidshowPp(){

//自動檢查但是會出現不可預期的錯誤

obj.show();//假設模板是Pp,可以調用Pp的變量和方法,在java中需要TextendPpT才能調用方法

templatetypenameT,typenameR

classCTest{

public:

Tm_name;

Rm_age;

CTest(Tname,Rage){

this-m_name=name;

this-m_age=age;

voidshow(){

cout"showT:"m_name"R:"m_ageendl;

templatetypenameT,typenameR

voiddoWork(CTestT,RcTest){

cTest.show();

templatetypenameT

voiddoWork2(Tt){

t.show();//在java中必須是Textendxxxx

//繼承模板問題和java是一樣的

templatetypenameT

classBase{

public:

Tt;

//確定的類型或者模板

templatetypenameT,typenameR

classSon:BaseR{

public:

Tt1;

intmain(){

CTeststring,inttest("后端碼匠",28);//showT:后端碼匠R:28

doWork(test);

doWork2CTeststring,int(test);//顯示調用

doWork2(test);//自動推導

ObjTempPptemp;

temp.showPp();//Ppshow可以調用傳遞過來的模板的方法

//自動類型推導,在類模板上不可以使用,無法推導出具體的類型

Personint,stringp(100);

coutp.getA()endl;

return0;

}

showT:后端碼匠R:28

showT:后端碼匠R:28

showT:后端碼匠R:28

Ppshow

0

實現一個模板類ArrayList類似Java的列表實現:

注意在之前學習的.h和.cpp分開的方式,不支持模板,一般模板的部分都會合并到.h文件中。

#includeiostream

#includecstring

usingnamespacestd;

#ifndefCPPDEMO_ARRAYLIST_H

#defineCPPDEMO_ARRAYLIST_H

templatetypenameT

classArrayList{

public:

intd=11;

ArrayList(){

this-size=16;

this-realSize=0;

this-arr=newT[this-size];

//explicit不能通過隱式調用

explicitArrayList(intcapacity){

this-size=capacity;

this-realSize=0;

//在堆區申請數組

this-arr=newT[this-size];//在堆中開辟的一塊空間存儲的是一個int[size]數組,arr指向數組的首地址

//拷貝函數

ArrayList(constArrayListarrayList){

this-size=arrayList.size;

this-realSize=arrayList.realSize;

this-arr=newT[arrayList.size];

//將數組的值賦值到arr中

for(inti=0;ithis-size;++i){

this-arr[i]=arrayList.arr[i];//arrayList.arr[i]他也是指針this-arr[i]是指針

//析構函數

~ArrayList(){

if(this-arr!=nullptr){

delete[]this-

this-arr=nullptr;

voidadd(Tval){

add(val,this-realSize);

voidadd(Tval,intindex){

if(index0||indexsize){

return;

//判斷容量是否夠大不夠進行擴容

if(this-realSize=size*0.75){

resize();

this-arr[index]=val;//等價于*((this-arr)+index)=val

this-realSize++;//數據量大小+1

Tget(intindex){

if(index0||index=realSize){

return-1;

returnthis-arr[index];

Tremove(intindex){

if(index0||index=realSize){

return-1;

//如何移除呢?循環往前移動

intresult=this-arr[index];

for(inti=index;isize-1;++i){

this-arr[i]=this-arr[i+1];

this-realSize--;

//判斷縮減容量

returnresult;

//const定義為常函數

intgetLength()const{

//realSize=realSize-1;這樣會報錯不能修改函數內部的所有變量

c=11;//mutable修飾的變量可以在常函數中修改

returnrealSize;

boolisEmpty()const{

returnrealSize==0;

voidresize(){

intnetLength=size*2;

T*p=newT[netLength];

//拷貝數據

for(inti=0;isize;++i){

*(p+i)=this-arr[i];

//釋放之前的數組

delete[]this-

//重新賦值

this-arr=p;

this-size=netLength;

voidtoString(){

cout"[";

for(inti=0;irealSize;++i){

coutarr[i]",";

cout"]"endl;

private:

intsize{};//容器的大小

intrealSize{};//真實的數組長度

T*arr;//這里不能使用數組,因為數組名是arr指針常量,不能對arr重新賦值,指針是指針變量,而數組名只是一個指針常量

mutableintc=10;//可以在常函數中修改的變量需要使用mutable進行修飾

intmain(){

ArrayListintarrayList;

arrayList.add(1);

溫馨提示

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

評論

0/150

提交評論