资源预览内容
第1页 / 共75页
第2页 / 共75页
第3页 / 共75页
第4页 / 共75页
第5页 / 共75页
第6页 / 共75页
第7页 / 共75页
第8页 / 共75页
第9页 / 共75页
第10页 / 共75页
亲,该文档总共75页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第八章 多态性1n8.1 多态性概述 n8.2 运算符重载 n8.3 虚函数 n8.4 抽象类 28.1 多态性概述 多态性:一个名字多种方法图8-1 多态性为用户提供单一接口示意图3nC系统能临时根据函数调用中参数的不 同决定调用哪一个同名函数n函数重载:n实现编译时的多态性静态联编n区分依据:形参类型、个数不同n虚函数:n实现运行时的多态性动态联编n区分依据:对象的类型n只能用于继承48.2 运算符重载 例1:一个日期类对象加上若干天数,应该仍是日期Date d1(2001,12,1);int s=10;应能得到:20011211即运算: d1=d1+10; 是合理的C的语法问题:“”运算的操作数是什么?“”只定义了对内部数据类型int char(字符) float(double)进行算术运算5例2:字符串相加操作char a10=“Hello“;char b10=“World“;char c20=a+b;严重的语法问题: 1. 类型不一致; 2. a+b无意义 3. “”不作串连接运算68.2.1 运算符重载的规则1. 一元操作符与二元操作符一元操作符:+ - public:CPoint ( int vx, int vy) x = vx ; y = vy ;CPoint ( ) x = 0; y = 0;CPoint operator + ( CPoint p1);CPoint operator - ( CPoint p1);void Print () cout class CMyClass public:CMyClass ( int x = 0 ) ;CMyClass void Print ( ) ;private:int m ; ; CMyClass:CMyClass ( int x ) m = x ; 12CMyClass return *this ; void CMyClass : Print ( ) cout operator(形参表) 函数体; 重载运算符的返 回值类型,即运 算结果类型关键字要重载的运算符名称重载运算符所需要的参 数和类型8.2.3 运算符重载为成员函数友元函数关键字在为参数表填写操作数时,与用成员函数重载操作 符不同。若操作符是一元的,则参数表中有一个操 作数用来充当这唯一的操作数;若运算符是二元的 ,则参数中必须有两个操作数。14上例中:class CPoint int x, y ; public:/ friend CPoint operator + ( CPoint p1, CPoint p2); ;CPoint operator + ( CPoint p1, CPoint p2) CPoint p;p.x = p1.x + p2.x; p.y = p1.y + p2.y;return p; 15用友元函数重载运算符#include using namespace std; class Complex double r,l; public: Complex(double k=0, double i=0):r(k),l(i) /空的构造函数 void Display() cout void main() Complex c1(3,4),c2(5,6),c3; c1.Display(); c2. Display(); c3. Display(); cout class Complex double real,imag;public:Complex(double r=0, double i=0):real(r),imag(i) /空的构造函数Complex(Complex cout class base/定义基类base public:void who() coutwho();ptr= ptr-who();ptr= ptr-who();obj1.who(); obj2.who(); n此例在main()函数中定义了一个基类对象 obj,和两个派生类对象obj1与obj2,又定 义了一个指向基类对象的指针ptr。 39n此程序的意图是用ptr指针分别指向不同 的对象,以便执行不同对象所对应的类 的成员函数。当ptr指向obj对象时,ptr- who()调用base类的成员函数who(); 当ptr指向obj1对象时,我们希望ptr- who()调用derive1类的成员函数who() ;而当ptr指向obj2对象时,则希望ptr- who()调用derive2类的成员函数who() 。此程序执行后实际得到的结果为40nthis is the class of base!(a)nthis is the class of base!(b)nthis is the class of base!(c)nthis is the class of derive1!(d)nthis is the class of derive2!(e)41n在运行结果中,(a)、(d)和(e)与所预想的 相符,而(b)和(c)却不是希望得到的。这说 明,不管指针ptr当前指向哪个对象(是基 类对象还是派生类对象),ptr-who()调用 的都是基类中定义的who()函数。也就是 说,通过指针引起的普通成员函数调用, 仅仅与指针的类型有关,而与指针正指向 什么对象无关。在这种情况下,必须采用 显式的方式调用派生类的函数成员。 42例如:obj1.who()或obj2.who() 或者是采用对指针的强制类型转换的方法,例如: (derive1*)ptr)-who()或(derive2*)ptr)-who()本来使用对象指针是为了表达一种动态的性质 ,即当指针指向不同对象时执行不同的操作,现 在看来并没有起到这种作用。要实现这种功能, 就需要引入虚函数的概念。这里,只需将基类的 who()函数声明为虚函数即可。 438.3.2 虚函数的定义1.虚函数的定义n虚函数的定义是在基类中进行的。它是在基类中 需要定义为虚函数的成员函数的声明中冠以关键 字virtual。n当基类中的某个成员函数被声明为虚函数后,此 虚函数就可以在一个或多个派生类中被重新定义 ,在派生类中重新定义时,其函数原型,包括返 回类型、函数名、参数个数、参数类型以及参数 的顺序都必须与基类中的原型完全相同。44一般虚函数的定义语法如下:virtual(形参表) 函数体 n其中,被关键字virtual说明的函数为虚函 数。特别要注意的是,虚函数的声明只能 出现在类声明中的函数原型声明中,而不 能出现在成员的函数体实现的时候。45Virtual虚函数n编译阶段,编译器不按静态类型生成调用 此函数的版本;为它生成虚函数表(存放 同名,同参,同返回值的虚函数地址)。n程序运行时,根据对象实际类型,查表调 用相应的虚函数。n要实现多态性,必须在基类中定义虚函数n要实现多态性,派生类中要定义同名、同 参数的函数(virtual说明可以省略)46【例8-5】使用虚函数例题。 #include class base/定义基类base public:virtual void who() /虚函数声明 coutwho();ptr= ptr-who();ptr= ptr-who();49n分析一下上面这个程序,在基类中对void who()进行了虚函数声明,这样,在其派生 类中就可以重新定义它。在派生类derive1 和derive2中分别重新定义void who()函数 ,注意,此虚函数在派生类中重新定义时 不再需要virtual声明,此声明只在其基类中 出现一次。在void who()函数被重新定义时 ,其函数的原型与基类中的函数原型必须 完全相同。 50n在main()函数中,定义了一个指向基类类 型的指针,它也被允许指向其派生类。在 执行过程中,不断改变它所指向的对象, ptr-who()就能调用不同的版本。虽然都 是ptr-who()语句,但是,当ptr指向不同 的对象时,所对应的执行动作就不同。由 此可见,用虚函数充分体现了多态性。并 且,因为ptr指针指向哪个对象是在执行 过程中确定的,所以体现的又是一种动态 的多态性。51函数名返回值参数束定时间适用范围语义相关性 虚函数同同运行时时派生类类一组类组类 似函 数 重载函数可不同不同编译时编译时任意可语义语义 无关2.虚函数和重载函数l一组虚函数中,两个虚函数仅返回值不同,参数 和名字相同,编译错。523多继承中的虚函数在多继承中由于派生类是由多个基类 派生而来的,因此,虚函数的使用就不像 单继承那样简单。请看下面的例题。 【例8-6】多继承中使用虚函数例题。53#include class base1/定义基类base1 public:virtual void who() /函数who()为虚函数 coutwho();ptr2= ptr2-who();ptr1= ptr1-who();ptr2= ptr2-who(); 55此时,程序执行的结果为n从上面的例子看出,派生类derive中的函 数who()在不同的场合呈现不同的性质。 如相对base1路径,由于在base1中的 who()函数前有关键字virtual,所以它是一 个虚函数;相对于base2派生路径,在 base2中的who()函数为一般函数,所以, 此时它只是一个重载函数。56n当base1类指针指向derive类对象obj3时, 函数who()就呈现出虚特性;当base2类指 针指向derive类对象obj3时,函数只呈现一 般的重载特性。n若一个派生类,它的多个基类中有公共的 基类,在公共基类中定义一个虚函数,则 多重派生以后仍可以重新定义虚函数,也 就是说,虚特性是可以传递的。请看下面 的例题。 57【例8-7】多继承中虚特性的传递例题。 #include class base/定义基类base public:virtual void who()/定义虚函数coutwho();ptr2= ptr2-who(); 此时,程序执行的结果为60n从本例题可以看出,虚特性是可以传递 的。base类作为base1和base2类的直接 基类,它的成员函数who()被声明为虚函 数,则base1和base2类中的who()都具有 虚特性,即均为虚函数;而derive类为 base1和base2类的派生类,因此,它的 成员函数who()也为虚函数。 618.3.3 虚函数的限制n如果我们将所有的成员函数都设置为虚 函数,当然是很有益的。它除了会增加 一些额外的资源开销,没有什么坏处。 但设置虚函数须注意以下几点。 只有成员函数才能声明为虚函数。因为 虚函数仅适用于有继承关系的类对象, 所以普通函数不能声明为虚函数。62虚函数必须是非静态成员函数。这是因为静态成员 函数不受限于某个对象。内联函数不能声明为虚函数。因为内联函数不能在 运行中动态确定其位置。构造函数不能声明为虚函数。多态是指不同的对象 对同一消息有不同的行为特性。虚函数作为运行过 程中多态的基础,主要是针对对象的,而构造函数 是在对象产生之前运行的,因此,虚构造函数是没 有意义的。析构函数可以声明为虚函数。析构函数的功能是在 该类对象消亡之前进行一些必要的清理工作。析构 函数没有类型,也没有参数,和普通成员函数相比 ,虚析构函数情况略为简单些。 63虚析构函数的声明语法如下: virtual类名 例如: class B public: / virtua
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号