010多态.docx

上传人:b****8 文档编号:10957398 上传时间:2023-02-24 格式:DOCX 页数:26 大小:22.44KB
下载 相关 举报
010多态.docx_第1页
第1页 / 共26页
010多态.docx_第2页
第2页 / 共26页
010多态.docx_第3页
第3页 / 共26页
010多态.docx_第4页
第4页 / 共26页
010多态.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

010多态.docx

《010多态.docx》由会员分享,可在线阅读,更多相关《010多态.docx(26页珍藏版)》请在冰豆网上搜索。

010多态.docx

010多态

多态

一、什么是多态

不同对象接收到相同的消息时,产生不同的结果。

二、多态的分类

有通用多态(参数多态,包含多态)和专用多态(重载多态,强制多态)。

参数多态:

模板;包含多态:

虚函数;重载多态:

函数及运算符重载;强制多态:

强制类型转换。

三、多态的实现

编译时的多态(重载实现。

对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

)和运行时的多态(虚函数实现。

直到系统运行时,才根据实际情况决定实现何种操作。

)。

编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

四、虚函数多态

1.无虚函数利用指针时产生的歧义问题

#include“iostream.h”

classbase

{

public:

voidshow()

{cout<<”基类的内容\n”;}

};

classsub:

publicbase

{

public:

voidshow()

{cout<<”子类的内容\n”;}

};

main()

{

basea,*p;

subb,*j;

p=&a;

j=&a;

p->show();

p=&b;

p->show();//((sub*)p)->show();可显示子类内容

}

通过指针引起的普通函数调用,仅与指针(或引用)的类型有关,而与此时指针正指向的对象无关。

一个指向基类的指针可用来指向从基类公有派生的任何的对象,它是C++实现运行时多态的关键途径。

2.为了使用对象指针表达动态性质,引入虚函数

虚函数是为了实现多态,也就是要用基类指针调用派生类的方法,虚函数常常出现在一些抽象接口类定义里。

定义格式:

virtual函数类型函数名(形参表)

{函数体}

虚函数在基类中定义,在派生类(public类型)中重载,在派生类中重写时可以加virtual也可以不加。

虚函数被多层次公有继承也仍为虚函数,也可以在派生类中重定义。

#include“iostream.h”

classbase

{

public:

virtualvoidshow()

{cout<<”基类的内容\n”;}

};

classsub:

publicbase

{

public:

voidshow()

{cout<<”子类的内容\n”;}

};

main()

{

basea,*pc;

subb;

pc=&a;

pc->show();

pc=&b;

pc->show();//随指针移动,动态实现了单界面,多实现版本

}

3.虚析构函数

一个类拥有虚析构函数表明它是一个基类。

用虚析构函数是为了解决当用基类的指针指向派生类时,如果删除此指针,要调用派生类的析构函数(虚函数的作用)。

如:

base*p=newsub();

deletep;//调用派生类的析构函数

有时,一个类想跟踪它有多少个对象存在。

一个简单的方法是创建一个静态类成员来统计对象的个数。

这个成员被初始化为0,在构造函数里加1,析构函数里减1。

比如,设想在一个军事应用程序里,有一个表示敌人目标的类:

classenemytarget

{

public:

enemytarget(){++numtargets;}

~enemytarget(){--numtargets;}

staticintnumoftargets(){returnnumtargets;}

private:

staticintnumtargets;

};

敌人的坦克是一种特殊的敌人目标,所以会很自然地想到将它抽象为一个以公有继承方式从enemytarget派生出来的类:

classenemytank:

publicenemytarget

{

public:

enemytank(){++numtanks;}

~enemytank(){--numtanks;}

staticintnumoftanks(){returnnumtanks;}

private:

staticintnumtanks;

};

但这段代码当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。

intmain()

{

enemytarget*p=newenemytank();

cout<<”现在敌军坦克数量是:

”<

:

numoftanks()<

deletep;

p=NULL;

cout<<”现在敌军坦克数量是:

”<

:

numoftanks()<

return0;

}

但一个类中如果不包含虚函数,则说明该类一般不做基类,则无必要定义虚析构函数。

因为虚函数会产生一个指向虚函数表函数指针,使程序点用额外的空间。

五、纯虚函数和抽象类

1.纯虚函数是一个在基类中说明的虚函数,它在基类中没有定义,但要在子类中进行定义。

格式:

virtual函数类型函数名(参数表)=0;

例:

#include“iostream.h”

classcircle

{

public:

voidsetr(intx)

{r=x;}

virtualvoidshow()=0;

protected:

intr;

};

classarea:

publiccircle

{

public:

voidshow()

{cout<<”面积是:

”<<3.14*r*r<

};

classmeter:

publiccircle

{

public:

voidshow()

{cout<<”周长是:

”<<2*3.14*r<

};

main()

{

circle*p;

areaobj1;

meterobj2;

obj1.setr(10);

p=&obj1;

p->show();

obj2.setr(20);

p=&obj2;

p->show();

}

2.抽象类

至少含有一个纯虚函数的类就叫抽象类。

(1)抽象类只能做为其它类的基类,不能实例化对象。

(2)不能从具体类派生抽象类。

(3)派生类中没有重定义纯虚函数,则它仍为抽象类。

六、重载多态(运算符重载)

1.运算符重载目的:

为解决非C++语言预定义的新数据类型的运算,它是对已有的运算符赋予了多重含义,同一个运算符作用于不同类型的数据导致不同类型的行为。

比如复数的运算。

包括单目运算符和双目运算符重载。

2.C++可以重载的运算符

+

-

*

/

%

>

>=

<

<=

==

!

=

&&

||

!

^

&

|

~

<<

>>

=

+=

-=

*=

/=

%=

^=

&=

|=

<<=

>>=

++

--

->*

->

[]

()

new

delete

不可以重载的运算符:

.

.*

:

:

?

:

sizeof

3.运算符重载原则:

重载之后运算符的优先级和结合性不会发生改变;运算符重载是针对新类型数据的实际需要,对原有的运算符进行适当的改造,所以一般来讲,重载的功能应当与原有功能相类似,不能改变原运算符的操作对象个数,同时至少要有一个操作对象是自定义类型。

4.运算符重载必须写一个运算符函数,函数格式(也可以是友元函数,但new和delete除外):

类名operator运算符()

{//相关功能语句;}

5.单目运算符的重载

单目运算符中++和—重载格式为:

类名operator运算符();//前置重载

类名operator运算符(int);//后置重载

例:

取负运算“-”的重载:

对某一复数取负。

#include“iostream.h”

classcomplex

{

public:

complexoperator-();

voidinput(floatr,floatv);

voiddisp();

private:

floatrpart;

floatvpart;

};

complexcomplex:

:

operator-()

{

rpart=-rpart;

vpart=-vpart;

return*this;

}

voidcomplex:

:

input(floatr,floatv)

{

rpart=r;

vpart=v;

}

voidcomplex:

:

disp()

{

cout<<”取负后的复数为:

\n”;

if(vpart>=0)

cout<

else

cout<

}

main()

{

complexA;

A.input(10,25);

A=-A;//隐式调用

//A.operator-();显式调用

A.disp();

}

例:

++运算前置重载

#include“iostream.h”

classexam

{

public:

examoperator++();

voidinput(int,int);

voidoutput();

private:

inta;

intb;

};

examexam:

:

operator++()

{

++a;

++b;

return*this;

}

voidexam:

:

input(intx,inty)

{

a=x;b=y;

}

voidexam:

:

output()

{

cout<<”a:

”<

”<

}

main()

{

examex,exx;

ex.input(1,2);

++ex;

ex.output();

ex.operator++();

ex.output();

exx=++ex;

exx.output();

}

例:

--后置重载

#include“iostream.h”

classnumber

{

public:

numberoperator--(int);

voidinput(int,int);

voiddisp();

private:

inta,b;

};

numbernumber:

:

operator--(int)

{

numbertmp;

tmp=*this;

a--;

b--;

returntmp;

}

voidnumber:

:

input(intx,inty)

{

a=x;b=y;

}

voidnumber:

:

disp()

{

cout<<”a:

”<

”<

}

main()

{

numbern1,n2;

n1.input(10,20);

n1--;

n1.disp();

n1.operator--(0);

n1.disp();

n2=n1--;

n2.disp();

n1.disp();

}

6.双目运算符的重载

格式:

类名operator运算符(形参表);

例:

两个字符串的连接运算

#include"iostream"

#include"string.h"

usingnamespacestd;

classstrings

{

private:

charname[256];

public:

stringsoperator+(strings&a);

strings(char*b)

{

strcpy(name,b);

}

strings(){}

~strings(){}

voidout()

{cout<

};

stringsstrings:

:

operator+(strings&a)

{

stringss;

strcpy(s.name,name);

strcat(s.name,a.name);

returns;

}

intmain()

{

stringsstr1="abcd",str2="cdef";

stringsb;

b=str1+str2;

b.out();

return0;

}

例:

两个复数相加运算。

#include"iostream.h"

classcomplex

{

public:

complexoperator+(complexa);

voidinput(floatm,floatn);

voidoutput();

private:

floati,j;

};

complexcomplex:

:

operator+(complexa)

{

complext;

t.i=i+a.i;

t.j=j+a.j;

returnt;

}

voidcomplex:

:

input(floatm,floatn)

{

i=m;j=n;

}

voidcomplex:

:

output()

{

if(j>=0)

cout<

else

cout<

}

main()

{

complexa1,a2,a3;

a1.input(10,20);

a2.input(5,-5);

a3=a1+a2;

a3.output();

}

例:

重载new和delete运算符。

例1:

#include"iostream.h"

#include"stddef.h"

classmemmanager

{

public:

void*operatornew(size_tsize);

void*operatornew(size_tsize,chartag);

voidoperatordelete(void*p);

};

void*memmanager:

:

operatornew(size_tsize)

{

cout<<"new1operator"<

char*s=newchar[size];

//cout<

//*s='a';

returns;

}

void*memmanager:

:

operatornew(size_tsize,chartag)

{

cout<<"new2operator"<

char*s=newchar[size];

*s=tag;

returns;

}

voidmemmanager:

:

operatordelete(void*p)

{

cout<<"deleteoperator"<

delete(char*)p;

//char*s=(char*)p;

//delete[]s;

}

voidmain()

{

memmanager*m=newmemmanager();

deletem;

memmanager*n=new('B')memmanager;

deleten;

}

例2:

#include"iostream.h"

#include"malloc.h"

classrect

{

private:

intlength,width;

public:

rect(intl,intw)

{length=l;width=w;}

voiddisp()

{cout<<"矩形的面积是:

"<

void*operatornew(size_tsize);

voidoperatordelete(void*p);

};

void*rect:

:

operatornew(size_tsize)

{

cout<<"重载new运算符分配内存\n";

returnmalloc(size);

}

voidrect:

:

operatordelete(void*p)

{

cout<<"重载delete运算符释放内存\n";

free(p);

}

voidmain()

{

rect*p=newrect(5,9);

p->disp();

deletep;

}

七、类型转换

1.隐式转换

2.显式转换

格式:

(类型名)表达式

八、模板

1.函数模板

格式:

template

返回类型函数名(模板形参)

{

函数体;

}

例:

将求两数中大的数定义为函数模板

template//class可写为typename

Tmax(Tx,Ty)

{

return(x>y)?

x:

y;

}

 

模板的使用:

(1)一个类型参数的函数模板

#include“iostream.h”

template

Tmax(Tx,Ty)

{

return(x>y)?

x:

y;

}

main()

{

inti1=3,i2=45;

floatf1=12.5,f2=34.6;

doubled1=7,d2=90;

charc1=’a’,c2=’Y’;

cout<

cout<

cout<

cout<

}

(2)有两个参数的函数模板

#include“iostream.h”

template

//template

voidoutput(type1x,type2y)

{

cout<

}

main()

{

inti1=3,i2=45;

floatf1=12.5,f2=34.6;

doubled1=7,d2=90;

charc1=’a’,c2=’Y’;

output(i1,i2);

output(f1,f2);

output(d1,d2);

output(c1,c2);

output(i1,c2);

output(i2,f1);

output(f2,d2);

}

2.类模板

格式:

template

class类名

{

//……

};

类模板的使用:

类名<实际的类型>对象名;

#include"iostream.h"

//#include“string”

template//template

classABC

{

public:

voidinput(typea);

//{x=a;}

voidoutput()

{cout<

private:

typex;

};

//template

//voidABC:

:

input(typea)

//{x=a;}

//template

//voidABC:

:

output()

//{cout<

main()

{

ABCobj1;

obj1.input(23);

obj1.output();

ABCobj2;

obj2.input(45.5);

obj2.output();

ABCobj3;

obj3.input('E');

obj3.output();

//ABCobj4;

//obj4.input(“这是一个模板的示例子”);

//obj4.output();

}

作业:

九、练习

1.关于虚函数,正确的描述是:

A.构造函数不能是虚函数()

B.析构函数不能是虚函数

C.虚函数可以是友元函数

D.虚函数可以是静态成员函数

2.要实现动态联编,派生类中的虚函数:

()

A.返回的类型可以与虚函数的原形不同

B.参数个数可以与虚函数的原形不同

C..参数类型可以与虚函数的原形不同

D.以上都不对

3.如果在基类中将show声明为不带返回值的纯虚函数,正确的写法是:

()

A.virtualshow()=0;

B.virtualvoidshow();

C.virtualvoidshow()=0;

D.voidshow()=0virtual;

4.下面的程序段中虚函数被重新定义的方法正确吗?

为什么?

classbase{

public:

virtualintf(inta)=0;

//...

};

classderived:

publicbase{

public:

intf(inta,intb)

{

returna*b;

}

//...

};

5.分析以下程序的运行结果:

#include

classStock

{

public:

voidprint()

{

cout<<”Stockclass.\n”;

}

};

classDerl_Stock:

publicStock

{

public:

voidprint()

{

cout<<”Der1_Stockclass.\n”;

}

};

classDer2_Stock:

publicStock

{

public:

voidprint()

{

cout<<”Der2_Stockclass.\n”;

}

};

voidmain()

{

Stocks1;

Stock*ptr;

Der1_Stockd1;

Der2_Stockd2;

ptr=&s1;

ptr->print();

ptr=&d1;

ptr->print();

ptr=&d2;

ptr->print();

}

6.修改上一题的程序,使运行结果为:

Stockclass.

Der2_Stockclass.

Der2_Stock

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 畜牧兽医

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1