1、什么是虚函数?
虚函数是在类中由virtual关键字声明的成员函数,并且每一个含有虚函数的类都会至少有一个与之对应的虚函数表,用来存放该类所有虚函数对应的函数指针。
所有虚函数地址都会存放在所属类的虚函数表中,子类会继承父类的虚函数表,若是子类中有与父类相同的虚函数,则会构成重写,此时子类的虚函数指针将会覆盖父类的虚函数指针,若子类中有新增的虚函数,则也会按顺序添加在子类的虚函数表中。
2、虚函数表是如何构造和继承的
(1)基类虚函数表的构造
在基类声明中找到所有的虚函数,按照声明顺序进行编码,然后按照编码顺序为基类构建一个虚函数表,虚函数表中的内容就这这些指向这些虚函数的函数指针。
(2)子类中虚函数表的构建与继承
首先将基类中的虚函数表复制到子类的虚函数表指针中,若子类重写了基类的虚函数,则将子类中的虚函数表中对应的虚函数的函数指针更新为子类的虚函数的函数指针,若子类中有新增的虚函数,则将新增虚函数的函数指针添加找虚函数表的后面。
3、虚函数的实现机制
一个类中出现虚函数,就会产生相应的虚函数表,来存放指向虚函数的函数指针,如果一个子类继承基类,那么首先将基类中的虚函数表复制到子类的虚函数表指针中,若子类重写了基类的虚函数,则将子类中的虚函数表中对应的虚函数的函数指针更新为子类的虚函数的函数指针,若子类中有新增的虚函数,则将新增虚函数的函数指针添加找虚函数表的后面, 所以这样通常只有在运行程序时才能确定对象的类型。由此说明编译器对虚函数的使用动态联编。
4、动态联编与静态联编
静态联编:在程序编译期间已经确定了程序的行为。比如:函数重载
动态联编:在程序运行期间才能确定程序的行为以及调用的函数
5、多态
多态就是指不同的对象完成同一行为时会产生不同的状态。
6、多态的产生条件
必须通过基类的指针或者引用调用虚函数;
被调用的函数必须是虚函数(有virtual关键字);
派生类必须对基类的虚函数进行重写(函数名、返回值类型、参数列表均相同)。
7、为什么要将析构函数定义成虚函数
因为子类的成员是有两部分构成,一部分是继承自父类的成员,一部分是自己定义的,如果析构函数不定义成虚函数,则子类的析构函数只能析构自己定义的这一部分,而继承自父类的成员是无法析构的,这样就会造成只析构一部分的情况,会造成资源泄漏,所以要将析构函数定义成虚函数。
8、哪些函数不能是虚函数,并说明理由?
(1)inline函数:因为inline函数没有地址,无法把地址放到虚函数表中。
(2)静态成员函数:因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚表,所以静态成员函数无法放进虚函数表。
(3)构造函数:因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
9、如果在构造函数调用一个类的虚函数会发生什么?
这样可能会出现问题,因为对象的构造是从基类开始构造,然后才是派生类。所以,如果调用的是继承类的虚函数的话,此虚函数将访问一个未被完全构造的对象
10、 对象访问普通函数快还是虚函数更快?
首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。