当前位置: 首页 > 科技观察

EasyC++,动态绑定

时间:2023-03-11 21:39:32 科技观察

静态绑定与动态绑定当我们在程序中调用函数时,应该执行哪个代码块?将源代码中的函数调用解释为执行特定函数代码块的过程称为函数名绑定(binding)。在C语言中,这很简单,因为每个函数名对应不同的函数。在C++中,函数重载的支持使这项任务变得更加复杂。编译器必须查看函数的参数和函数名来确定。好在函数和参数的选择是在编译时确定的,所以这部分链接可以在编译时完成。该链接称为静态链接。使用虚函数,任务变得更加复杂。因为编译时无法确定使用哪个函数,因为编译器不知道用户会选择哪种类型的对象。因此,编译器必须能够在程序运行时选择正确的虚函数,这称为动态绑定。指针和引用类型的兼容性在C++中,动态绑定是指通过指针和引用来调用方法,这是通过继承来控制的。前面说过,通过公有继承建立的is-a关系,让我们可以使用父类指针或者引用来指向子类的对象。在C++中,不允许将一种类型的地址赋值给另一种类型的指针。以下两种操作都是非法的。doublex=2.5;int*pi=&x;//Illegallong&r=x;//派生类引用或指向基类引用和指针的指针的非法转换称为向上转型(upcasting),这条规则是is-a关系的一部分.因为派生类继承了基类的所有数据成员和成员函数,基类成员可以进行的操作也适用于子类成员,所以向上转型是可传递的。反过来呢?将父类对象传递给子类指针?这个操作叫做向下转型(downcasting),没有转型是不允许的。由于is-a关系通常是不可逆的,派生类中往往会加入一些新的数据成员或方法,不能保证与父类对象兼容。虚函数的工作原理我们在使用虚函数的时候不需要知道其实现原理,但是了解其工作原理可以帮助我们更好的理解这个概念。另外,类似的实现细节在C++相关的开发面试中也经常被问到。通常,编译器处理虚函数的方式是:为每个对象添加一个隐藏成员,其中存储一个指向函数地址数组的指针,这个数组称为虚函数表。这个虚函数表存放的是当前类对象声明的虚函数的地址。让我们看一个例子:classHuman{private:...charname[40];public:virtualvoidshow_name();virtualvoidshow_all();...};classHero:publicHuman{private:...charpower[20];public:voidshow_all();virtualvoidshow_power();...};对于Human类型的对象,除了在类中声明的内容外,还会附加一个指向列表的指针,比如[1024,1222]。这里的1024和1222分别是show_name和show_all这两个功能代码块的地址。同样的,在Hero子类中也会有这样一个指向虚函数列表的指针。由于我们没有重载Hero子类中的show_name方法,所以Hero类型对象中列表的第一个元素仍然是1024。由于我们重载了show_all方法,并且我们添加了一个show_power的虚函数,它的虚函数列表可能是[1024、2333、3777]。简单的说,当我们调用虚函数时,编译器会先通过每个对象中的虚函数列表指针得到虚函数列表。然后在对应位置找到虚函数代码块的地址,最后执行。显然,这个过程涉及到维护虚函数地址表和函数执行时额外的查表操作,不仅消耗存储空间,也消耗性能。