虚函数总是在派生环境中使用,用虚函数实现动态多态性的一般步骤如下:在基类中定义虚函数;在派生类中定义与基类虚函数同名、同参数、同返回类型的成员函数,即派生类中的虚函数。分析以下程序的执行结果。图7.18类层次类BaseA包括虚函数bf(),类BaseB包括非虚函数bf(),类Derived是从类BaseA和类BaseB派生的,其中也包括函数bf()。解:设计的类层次如图7.2所示,普通函数printobject()采用动态绑定的方法。......
2023-11-07
多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。前者是在编译的过程中确定同名操作的具体操作对象,而后者则是在程序运行过程中才动态地确定具体操作对象。这种确定操作具体对象的过程就是绑定(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++使用虚函数来指定哪些函数需用动态绑定,其他非虚函数在编译连接阶段采用静态绑定方式,以节省执行阶段的时间。
有关C++程序设计基础教程的文章
虚函数总是在派生环境中使用,用虚函数实现动态多态性的一般步骤如下:在基类中定义虚函数;在派生类中定义与基类虚函数同名、同参数、同返回类型的成员函数,即派生类中的虚函数。分析以下程序的执行结果。图7.18类层次类BaseA包括虚函数bf(),类BaseB包括非虚函数bf(),类Derived是从类BaseA和类BaseB派生的,其中也包括函数bf()。解:设计的类层次如图7.2所示,普通函数printobject()采用动态绑定的方法。......
2023-11-07
程序设计是指设计、编写和调试程序的方法与过程。由于程序是软件的本体,因此软件的质量主要通过程序的质量体现,因此,研究一种切实可行的程序设计方法至关重要。继承是面向对象程序设计方法的一个重要标志,利用继承机制可以大大提高程序的可重用性和可扩充性。......
2023-11-07
声明函数模板的一般格式如下:template<模板参数表>函数返回值类型函数模板名(形参表)函数模板定义由关键字template开头,表示声明一个模板。模板参数表写在尖括号<>中,参数一般由关键字class或typename后加一个标识符构成。Class和typename的意义相同,表示后面的标识符是一个参数类型,代表一个潜在的标准类型或用户定义的类型。例如,定义max函数模板求两个数中的较大者。如下面的声明是错误的:可以看出,用函数模板比函数重载更方便,程序更简洁。......
2023-11-07
在类声明中,public、private和protected是关键字,称为成员访问限定符,它们分别表示公有、私有和保护的成员访问权限。其他函数不能访问该类的保护数据成员,也不能调用该类的保护成员函数。◇一旦给出了成员访问限定符,它后面的成员都具有这个成员访问权限,直到出现另一个成员访问限定符或类声明结束为止。类的成员函数可以访问类的所有成员,没有任何限制,而类的对象对类的成员的访问是受成员访问控制符制约的。......
2023-11-07
输入流是用流提取运算符实现的。流读取运算符通常会跳过输入流中的空格、tab键、换行符等等的空白字符。该函数读取num-1个字符后结束,或者遇到分隔符separator时结束。成员函数peek返回输入流中的下一个字符,但并不将其从输入流中删除。......
2023-11-07
类的每一个成员函数都有一个隐含的特殊指针,通常称为this指针,this指针的类型就是成员函数所属的类型。通常不显式使用this指针。this指针是C++实现封装的一种机制,它将对象和该对象调用的成员函数连接在一起,在外部看来,每个对象都拥有自己的成员函数。this指针一般用于返回当前对象自身。程序的执行结果如下:s1∶n=2s2∶n=1s3∶n=3this指针大量用于运算符重载成员函数设计中,这将在后面详细介绍。......
2023-11-07
图2.3对象stu1和stu2实际上,类Student的所有成员函数放在公用区中,每个函数代码有一个地址,类Student的每个对象中只存放自己的数据成员值和指向公共区中对应函数的地址,也就是说,类的成员函数是共享的,如图2.4所示,这种对象的存储空间分配方式不仅节省了存储空间,而且各个对象的数据成员分别存放,互不相干。......
2023-11-07
相关推荐