首页 > 代码库 > 虚函数相关问题分析

虚函数相关问题分析


1、静态联编和动态联编
将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。在C语言中,这非常简单,因为每个函数名都对应于一个不同的函数。在C++中,由于函数重载的缘故,这项任务更复杂。编译器必须查看函数参数以及函数名才能确定使用哪个函数。然而,C/C++编译器可以在编译过程中完成这种联编。在编译过程中进行联编称为静态联编,又称为早期联编。不过虚函数使这项工作变得更困难。因为编译器不知道将选择哪种类型的对象。所以,编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编,又称为晚期联编。
2、指针和引用类型兼容性
通常,C++不允许将一种类型的地址赋给另一种类型的指针,也不允许一种类型的引用指向另一种类型:
double x = 2.5;
int* pi = &x;//invalid assignment
long& r1 = x;//invalid assignment
不过,正如我们看到的,指向基类的引用或指针可以引用派生类对象,而不必进行显示类型转换。
将派生类引用或指针转换为基类引用或指针被称为向上强制转换,这使公有继承不需要进行显示类型转换。该规则是is-a关系的一部分。向上强制转换时可传递的,也就是说,如果A派生出B,B派生出C,则A指针或引用可以引用A对象、B对象或C对象。
相反的过程---将基类指针或引用转换为派生类指针或引用---称为向下强制转换,如果不使用显示类型转换,则向下强制转换时不允许的。
3、编译器对非虚方法使用静态联编,对虚方法使用动态联编。C++编译器默认使用静态联编。
4、虚函数的工作原理
通常,编译器处理虚函数的方法是:给每个对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表。虚函数表中存储了为类对象进行生明的虚函数的地址。例如,基类对象包含一个指针,该指针指向基类中所有虚函数的地址表。派生类对象将包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址;如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址也将被添加到虚函数表中。
5、注意事项
如果在派生类中重新定义函数,将不是使用相同的函数特征标覆盖基类声明,而是隐藏同名的基类方法,不管参数特征标如何。这将引出两条经验规则:
①如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针。这种特性被称为返回类型协变,因为允许返回类型随类类型的变化而变化。
②如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象将无法使用它们。