一、静态联编和动态联编当我们使用程序调用函数的时候究竟应该执行哪一个代码块呢将源代码中的函数调用解释为执行特定的函数代码块这个过程被称为函数名联编binding。在C语言当中这非常简单因为每个函数名都对应一个不同的函数。而在C当中由于支持了函数重载使得这个任务变得更加复杂。编译器必须要查看函数的参数以及函数名才能确定。好在函数的选择以及参数在编译的时候都是确定的所以这部分联编在编译时就能完成这种联编被称为静态联编。在有了虚函数之后 这个工作变得更加复杂。因为使用哪一个函数不能在编译时确定了因为编译器不知道用户将选择哪个类型的对象。所以编译器必须能在程序运行的时候选择正确的虚函数这被称为动态联编。1.指针和引用类型的兼容性在C当中动态联编与指针和引用调用方法相关这是由继承控制的。前文当中说过公有继承建立的is-a关系使得我们可以用父类指针或引用指向子类的对象。而在C当中是不允许将一种类型的地址赋值给另外一种类型的指针的。下面两种操作都是非法的123doublex 2.5;int*pi x;// 非法longr x;// 非法将派生类引用或指针转换成基类的引用和指针称为向上强制转换upcasting这种规则是is-a关系的一部分。因为派生类继承了基类当中所有的数据成员和成员函数因此基类成员能够进行的操作都适用于子类成员所以向上强制转换是可传递的。如果反过来呢将父类对象传递给子类指针呢这种操作被称为向下强制转换downcasting在不使用强制转换的前提下是不允许的。因为is-a关系通常是不可逆的派生类当中往往新增了一些数据成员或方法不能保证在父类对象上一样还能兼容。2.虚函数的工作原理我们在使用虚函数的时候其实可以不需要知道当中的实现原理但是了解了工作原理能够帮助我们更好地理解理念。另外在C相关的开发面试当中经常会问起类似的实现细节。通常编译器处理虚函数的方法是给每一个对象添加一个隐藏成员这个成员当中保存了一个指向函数地址数组的指针这种数组称为虚函数表。这个虚函数表中存储了当前这个类对象的声明的虚函数的地址我们来看一个例子12345678910111213141516171819classHuman {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]。简单来说当我们调用虚函数的时候 编译器会先通过每个对象中的虚函数列表指针拿到虚函数列表。然后在找到对应位置的虚函数代码块的地址最后进行执行。显然这个过程涉及到维护虚函数地址表以及函数执行时有额外的查表操作既带来了存储空间的消耗也带来了性能的消耗。