《多态性与虚函数.ppt》由会员分享,可在线阅读,更多相关《多态性与虚函数.ppt(28页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、多态性与虚函数,1 普通成员函数重载 2 派生类指针 3 虚函数 4 纯虚函数与抽象类,多态性是面向对象程序设计的重要特征之一。所谓多态性是指当不同的对象收到相同的消息时,产生不同的动作。C+的多态性具体体现在运行和编译两个方面,在程序运行时的多态性通过继承和虚函数来体现,而在程序编译时多态性体现在函数和运算符的重载上。,1 普通成员函数重载,在C+语言中,只有在声明函数原型时形式参数的个数或者对应位置的类型不同,两个或更多的函数就可以共用一个名字。这种在同一作用域中允许多个函数使用同一函数名的措施被称为重载(overloading)。函数重载是C+程序获得多态性的途径之一。 1.1 函数重载
2、的方法 函数重载要求编译器能够唯一地确定调用一个函数时应执行哪个函数代码,既采用哪个函数实现。确定函数实现时,要求从函数参数的个数和类型上来区分。这就是说,进行函数重载时,要求同名函数在参数个数上不同,或者参数类型不同。否则,将无法实现函数重载。,#include int square(int x) return x*x; double square(double y) return y*y; main() cout”The square of integer 7 is”square(7)endl; cout” The square of double 7.5 is”square(7.5)en
3、dl; return 0; ,例1:给出以下程序的运行结果。,此程序的运行结果为: The square of integer 7 is 49 The square of integer 7.5 is 56.25,#include const double PI=3.1415; double length(float r) return 2*PI*r; double length(float x,float y) return 2*(x+y); ,例2:用重载函数实现求圆和矩形的周长。,void main() float a,b,r; coutr; coutab; cout”矩形周长:”len
4、gth(r)endl; ,1.2 函数重载的表示形式 普通成员函数重载可表达为两种形式: 1. 在一个类说明中重载 例如:Show ( int , char ) ; Show ( char * , float ) ; 2. 基类的成员函数在派生类重载。有3种编译区分方法 (1)根据参数的特征加以区分 例如:Show ( int , char ) 与 Show ( char * , float ) 不是同一函数,编译能够区分,(2)使用“ : ”加以区分 例如:A : Show ( ) 有别于B : Show ( ) (3)根据类对象加以区分 例如:Aobj.Show ( )调用A : Show
5、 ( ) Bobj.Show ( )调用B : Show ( ),1.3 函数重载的注意事项 在C+语言中,编译程序选择相应的重载函数版本时函数返回值类型是不起作用的。不能仅靠函数的返回值来区别重载函数,必须从形式参数上区别开来。例如: void print(int a); void print(int a,int b); int print(float a); 这三个函数是重载函数,因为C+编译程序可以从形式参数上将它们区别开来。,但:int f(int a); double f(int a); 这两个函数就不是重载函数,编译程序认为这是对一个函数的重复说明,因为两个函数的形式参数个数与相应
6、位置的类型完全相同。 由typedef定义的类型别名并没有真正创建一个新的类型,所以以下程序段: typedef double money; double calculate(double income); money calculate(money income); 也是错误的函数重载。,同样道理,不同参数传递方式也无法区别重载函数,如: void func(int value); void func(int 也不能作为重载函数。 在程序中不可滥用函数重载,不适当的重载会降低程序的可读性。C+语言并没有提供任何约束限制重载函数之间必须有关联,程序员可能用相同的名字定义两个互不相关的函数。实际
7、上函数重载暗示了一种关联,不应该重载那些本质上有区别的函数,只有当函数实现的语义非常相近时才应使用函数重载。,1.4 函数重载的二义性 函数重载的二义性(ambiguity)是指C+语言的编译程序无法在多个重载函数中选择正确的函数进行调用。函数重载的二义性主要源于C+语言的隐式类型转换与默认参数。 在函数调用时,编译程序将按以下规则选择重载函数:如果函数调用的实际参数类型与一个重载函数形式参数类型完全匹配,则选择调用该重载函数;如果找不到与实际参数类型完全匹配的函数原型,但如果将一个类型转换为更高级类型后能找到完全匹配的函数原型,编译程序将选择调用该重载函数。所谓更高级类型是指能处理的值域较大
8、,如int转换为unsigned int,unsigned int转换为long,long转换为unsigned float等。,例如:int func(double d); countfunc(A); 虽未声明函数原型int func(char),但函数调用func(A)并不会产生任何问题,因为编译程序自动将字符A转换为double类型,然后调用函数int func(double)。 隐式类型转换是由C+编译程序自动完成的,这种类型转换是引起函数重载二义性的主要原因。在重载函数中使用默认参数也可能造成二义性。 C+还提供了一种更为灵活的多态性机制:虚函数。虚函数允许函数调用与函数体的联系在运
9、行时才进行。当一般的类型对应于不同的类型变种时,这个能力显得尤其重要。,2 派生类指针,指向基类和派生类的指针是相关的。 例如: A * p ;/ 指向类型 A 的对象的指针 A A_obj ;/ 类型 A 的对象 B B_obj ;/ 类型 B 的对象 p = ; char *Creature:KindOf() return Creature; ,使用下面的格式也是可以的: class Creature public: virtual char *KindOf()=0 return Creature; ;,4.2 抽象类 如果一个类中至少有一个纯虚函数,那么这个类被成为抽象类(abstrac
10、t class)。抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类中的纯虚函数可能是在抽象类中定义的,也可能是从它的抽象基类中继承下来且重定义的。 抽象类有一个重要特点,即抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。抽象类不能直接创建对象的原因是其中有一个或多个函数没有定义,但仍可使用指向抽象类的指针支持运行时多态性。,一个抽象类不可以用来创建对象,这只能用来为派生类提供了一个接口规范,派生类中必须重载基类中的纯虚函数,否则它仍将被看作一个抽象类。如果要直接调用抽象类中定义的纯虚函数,必须使用完全限定名,如上面的示例,要想直接调用抽象类Creature中定义的纯虚函数,应该使用
11、下面的格式: coutCreature:KindOf()endl; 上面的代码同时还给出了一种绕过虚函数机制的方法,即使用带有作用域限定符的完全限定函数名。,抽象类只能用作其他类的基类,抽象类不能建立对象。抽象类不能用作函数参数类型、函数返回值类型或显式转换的类型。可以声明抽象类的指针和引用。而且,如果在抽象类的构造函数中调用了纯虚函数,那么,其结果是不确定的。还有,由于抽象类的析构函数可以被声明为纯虚函数,这时,应该至少提供该析构函数的一个实现。一个很好的实现方式是的抽象类中提供一个默认的析构函数,该析构函数保证至少有析构函数的一个实现存在。如下面的例子所示:,class classname / 其他成员 public: classname()=0 / 在此添加析构函数的代码 ;,由于派生类的析构函数不可能和抽象类的析构函数同名,因此,提供一个默认的析构函数的实现是完全必要的。这也是纯虚析构函数和其他纯虚成员函数的一个最大的不同之处。一般情况下,抽象类的析构函数是有派生类的实现对象释放时由派生类的析构函数隐含的所调用的。 抽象类的主要作用是取若干类的共同行为,形成更清晰的概念层次。使用抽象类符合程序设计中的单选原则(single choice principle)。 从基类继承来的纯虚函数,在派生类中仍是虚函数。,