总结了一些常见的面试题,通过查阅资料给出了一些浅薄的解析,欢迎各位批评指教。
1. inliine函数可以实虚函数码?
不可以,因为inline函数没有地址,无法将他存放到虚函数表中。
2. 静态成员可以是虚函数吗?
不能,因为静态成员函数中没有this指针,使用::成员函数的嗲用用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。
3. 构造函数可以是虚函数吗?
不可以,因为对象中的虚函数指针是在对象构造的时候初始化的。
4. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
可以,最好将析构函数设置为虚函数最好是将父类的析构函数设置为虚函数 ,因为这样可以避免内存泄漏的问题。如果一个父类的指针指向了子类的的对象,并且父类的虚函数没有设置成虚函数,那么子类对象中的虚函数就没有实现多态,他只会调用父类的析构函数,不会调用子类的析构函数,但是他创建对象的时候调用了子类的构造函数,所以说就用子类的构造函数就应该该取调用他的析构函数,这样才能保证所有的必须释放的资源都是放了,才可以保证不会有内存泄漏。如果是多态的,就会先去调用子类的析构函数,然后再取调用父类的析构函数,这样子类和父类的资源就都可以释放。
5. 对象访问普通函数快还是虚函数快?
如果是普通对象,是一样快的,如果是指针对象或者是引用对象,调用普通函数更快一些,因为构成了多态,运行时调用虚函数要先到虚函数表中去查找。这样然后才拿到韩式的地址,这样就不如直接可以拿到函数地址的普通函数快。
6. 虚函数表时再什么阶段生成的?他存放在哪里?
虚函数时再编译阶段生成的,他一般存放再代码段,也就是常量区。
7. 执行下面这段代码的结果
#include <iostream>
using namespace std;class Base
{
public:virtual void x(){cout << "Base::x" << endl;}void y(){x();cout << "Base::y" << endl;}
};class Derive : public Base
{
public:virtual void x(){cout << "Derive::x" << endl;}void y(){cout << "Derive::y" << endl;}
};int main()
{Base* p = new Derive;p->y();return 0;
}
解析:很显然Derive继承了Base,并且实现了多态,但是只有x()是虚函数重写,y()只在子类中声明了虚函数,没有在父类中声名所以不能y()不是虚函数重写,而是对父类中的y()重定义,所以在p调用y()的时候直接调用Base中的y(),在Base的y()中调用了x(),由于x()在子类中构成了虚函数重写,所以调用子类中的x(),答案也就不晓而知了。
8. 是否可以将类中的所有成员函数都声明称为虚函数,为什么?
虚函数是在程序运行的时候通过寻址操作才能确定真正要调用的的函数,而普通的成员函数在编译的时候就已经确定了要调用的函数。这个两者的区别,从效率上来说,虚函数的效率要低于普通成员函数,因为虚函数要先通过对象中的虚标指针拿到虚函数表的地址,然后再从虚函数表中找到对应的函数地址,最后根据函数地址去调用,而普通成员函数直接就可以拿到地址进行调用,所以没必要将所有的成员函数声明成虚函数。
9. 虚函数表指针被编译器初始化的过程怎么理解的?
当类中声明了虚函数是,编译器会在类中生成一个虚函数表VS中存放在代码段,虚函数表实际上就是一个存放虚函数指针的指针数组,是由编译器自动生成并维护的。虚表是属于类的,不属于某个具体的对象,一个类中只需要有一个虚表即可。同一个类中的所有对象使用同一个虚表,为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在每个对象的头添加了一个指针,用来指向虚表,并且这个指针的值会自动被设置成指向类的虚表,每一个virtaul函数的函数指针存放在虚表中,如果是单继承,先将父类的虚表添加到子类的虚表中,然后子类再添加自己新增的虚函数指针,但是在VS编译器中我们通常看不到新添加的虚函数指针,是编译器故意将他们隐藏起来,如果是多继承,在子类中新添加的虚函数指针会存放在第一个继承父类的虚函数表中。
10. 多态的分类?
静态绑定的多态的是通过函数的重载来实现的。动态绑定的多态是通过虚函数实现的。
11. 为什么要引入抽象类和纯虚函数?
为了方便使用多态特性,在很多情况下由基类生成对象是很不合理的,纯虚函数在基类中是没有定义的,要求在子类必须加以实现,这种包含了纯虚函数的基类被称为抽象类,不能被实例化,如果子类没有实现纯虚函数,那么它他也是一个抽象类。
12. 虚函数和纯虚函数有什么区别?
从基类的角度出发,如果一个类中声明了虚函数,这个函数是要在类中实现的,它的作用是为了能让这个函数在他的子类中能被重写,实现动态多态。纯虚函数,只是一个接口,一个函数声明,并没有在声明他的类中实现。对于子类来说它可以不重写基类中的虚函数,但是他必须要将基类中的纯虚函数实现。虚函数既继承接口的同时也继承了基类的实现,纯虚函数关注的是接口的统一性,实现完全由子类来完成。
13. 什么是多态?他有什么作用?
多态就是一个接口多种实现,多态是面向对象的三大特性之一。多态分为静态多态和动态多态。静态多态包含函数重载和泛型编程,进程多态是程序调用函数,编译器决定使用哪个可执行的代码块。静态多态是由继承机制以及虚函实现的,通过指向派生类的基类指针或者引用,访问派生类中同名重写成员函数。堕胎的作用就是把不同子类对象都当作父类来看,可以屏蔽不同子类之间的差异,从而写出通用的代码,做出通用的编程,以适应需求的不断变化。