首页 理论教育C++程序设计基础教程:多态实现

C++程序设计基础教程:多态实现

【摘要】:多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。按照绑定进行阶段的不同,可以分为两种不同的绑定方法:静态绑定和动态绑定,这两种绑定过程中分别对应着多态的两种实现方式。有些多态类型,其同名操作的具体对象能够在编译、连接阶段确定,通过静态绑定解决,比如重载、强制和参数多态。包含多态操作对象的确定就是通过动态绑定完成的。分析以下程序的执行结果。

多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。前者是在编译的过程中确定同名操作的具体操作对象,而后者则是在程序运行过程中才动态地确定具体操作对象。这种确定操作具体对象的过程就是绑定(binding)。绑定是指计算机程序自身彼此关联的过程,也就是把一个标识符名和一个存储地址联系在一起的过程;用面向对象的术语讲,就是把一条消息和一个对象的方法相结合的过程。按照绑定进行阶段的不同,可以分为两种不同的绑定方法:静态绑定和动态绑定,这两种绑定过程中分别对应着多态的两种实现方式。

绑定工作在编译连接阶段完成的情况称为静态绑定。因为绑定过程是在程序开始执行之前进行的,因此有时也称为早期绑定或前绑定。在编译、连接过程中,系统就可以根据类型匹配等特征确定程序中操作调用与执行该操作代码的关系,即确定了某一个同名标识到底是要调用哪一段程序代码。有些多态类型,其同名操作的具体对象能够在编译、连接阶段确定,通过静态绑定解决,比如重载、强制和参数多态。

与静态绑定相对应,绑定工作在程序运行阶段完成的情况称为动态绑定,也称为晚期绑定或后绑定。在编译、连接过程中无法解决的绑定问题,要等到程序开始运行之后再来确定。包含多态操作对象的确定就是通过动态绑定完成的。

C++作为一种面向对象的程序设计语言,它既支持静态绑定,又支持动态绑定,C++的动态绑定依赖于运行时指针所指对象的实际类型,根据对象类型,程序自动计算并连接多态函数的入口地址。由此可见,动态绑定需要计算和连接多态函数入口的执行代码。这段代码不是由程序员编写的,而是由编译系统根据可能的对象类型自动生成的。

为了进一步讲述静态绑定和动态绑定的区别,可通过一个例子说明。

【例7.1】分析以下程序的执行结果。

解:上述程序的功能用于计算并显示本科生和研究生的学费。在程序中设计了一个UnderGraduate类,其中的calTotalFee()函数用于输入一个本科生的学号和姓名并计算学费,display()用于显示本科生的学费。PostGraduate类是从UnderGraduate类派生的,其中的calTotalFee()函数用于输入一个研究生的学号和姓名并计算学费,display()用于显示研究生的学费。在main()函数中,定义了一个UnderGraduate对象stu1和一个PostGraduate对象stu2。当调用fee(stu1)函数时,希望输出本科生stu1的学费;调用fee(stu2)函数时,希望输出研究生stu2的学费。程序的执行结果如下:

本科生收费

学号:101↙

姓名:张颖↙

学 费:5500

住宿费:2200

书报费:600

其 他:300(www.chuimin.cn)

总费用:8600

研究生收费

学号:301↙

姓名:王小华↙

学 费:5500

住宿费:2200

书报费:600

其 他:300

总费用:8600

从执行结果可以看到,执行fee(stu1)是正确的,而执行fee(stu2)错误,这里仍然输出一个本科生的学费。为什么出现这种情况呢?这是因为函数fee(UnderGraduate&stu)在编译时就确定其对象为UnderGraduate,所以调用的stu.calTotalFee()和stu.display()均为UnderGraduate类的成员函数。而我们希望在执行fee(s)时根据s对象来确定调用的是UnderGraduate类的成员函数还是PostGraduate类的成员函数。这里采用的是静态绑定。

解决这一问题的方法是采用动态绑定,即在执行fee(s)时根据s对象来自动确定调用相应的成员函数,而不是在编译时就确定哪个重载函数被调用。这种在运行时根据其类型确定调用哪个函数的方法称为运行时的多态性,也就是动态绑定。

C++使用虚函数来指定哪些函数需用动态绑定,其他非虚函数在编译连接阶段采用静态绑定方式,以节省执行阶段的时间。