[C++] C++虚函数原理

 

C++类成员函数分为静态成员函数非静态成员函数非静态成员函数又分为普通函数虚函数

Q: 为什么使用虚函数

A: 使用虚函数,我们可以获得良好的可扩展性。在一个设计比较好的面向对象程序中,大多数函数都是与基类的接口进行通信。因为使用基类接口时,调用基类接口的程序不需要改变就可以适应新类。如果用户想添加新功能,他就可以从基类继承并添加相应的新功能。

Q: 简述C++虚函数作用及底层实现原理

A: 要点是要答出虚函数表虚函数表指针的作用。

虚函数是用来实现动态绑定的。

C++中虚函数使用虚函数表和虚函数表指针实现,虚函数表是一个类的虚函数的地址表,用于索引类本身以及父类的虚函数的地址,假如子类重写了父类的虚函数,则对应在虚函数表中会把对应的虚函数替换为子类的函数的地址(子类中可以不是函数,但是必须同名);虚函数表指针存在于每个对象中(通常出于效率考虑,会放在对象的开始地址处),它指向对象所在类的虚函数表的地址;在多继承环境下,会存在多个虚函数表指针,分别指向对应不同基类的虚函数表。

 

虚函数表是每个(有虚函数的)对应一个。虚函数表指针是每个对象对应一个。

虚函数表里只能存放虚函数,不能存放普通函数。

如果一个函数不是虚函数,那么对它的调用(即该函数的地址)在编译阶段就会确定。调用虚函数的话(它的地址)要运行时才能确定。

虚函数的函数入口是动态绑定的。在运行时,程序根据基类指针指向的实际对象,来调用该对象对应版本的函数。(用该对象虚函数表指针找到其虚函数表,进而调用不同的函数。)(只有是虚函数的情况下才会这么做(用虚函数表指针去查虚函数表)。非虚函数直接就调用自己的。)

 

follow up:

1. 为什么需要虚析构函数?(什么情况下要用虚析构函数?)

在存在类继承并且析构函数中需要析构某些资源时,析构函数需要是虚函数。否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,造成内存泄露。

2. 一个对象访问普通成员函数和虚函数哪个更快?

访问普通成员函数更快,因为普通成员函数的地址编译阶段就已确定,因此在访问时直接调用对应地址的函数;

而虚函数在调用时,需要首先在虚函数表中寻找虚函数所在地址,因此相比普通成员函数速度要慢一些。

3. 析构函数一定是虚函数吗?

不一定。1. 虚函数效率相对要低一些;2. 有些类并没有子类,没必要用虚析构函数。

4. 内联函数、构造函数、静态成员函数可以是虚函数吗?

都不可以。

内联函数(inline)需要在编译阶段展开(在编译时就已经确定了),而虚函数是运行时动态绑定的,编译时无法展开,因此是矛盾的;

构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类的,因此不存在动态绑定的概念(先有父类才能有子类,构造父类的时候子类还不存在,子类都还没有怎么可能在父类里动态调用子类);

静态成员函数(static)是以为单位的函数,与具体对象无关,虚函数是与对象动态绑定的,因此是两个矛盾的概念;

5. 构造函数中可以调用虚函数吗?

 可以,但是没有意义,起不到动态绑定的效果。父类构造函数中调用的仍然是父类版本的函数,子类中调用的仍然是子类版本的函数。

6. 简述C++中虚继承的作用及底层实现原理?

虚继承用于解决多继承条件下的菱形继承问题,底层实现原理与编译器相关,一般通过虚基类指针实现,即各对象中只保存一份父类的对象,多继承时通过虚基类指针引用该公共对象,从而避免菱形继承中的二义性问题。

posted @ 2018-11-13 14:55  MH1092  阅读(325)  评论(0编辑  收藏  举报