MST

星途 面试题库

面试题:C++虚拟函数与普通成员函数在内存布局和调用开销上的差异

分析C++虚拟函数和普通成员函数在类的内存布局中有何不同,以及这种不同如何影响函数的调用开销,同时说明在何种场景下这种开销差异会对程序性能产生显著影响。
12.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

类的内存布局差异

  1. 普通成员函数:普通成员函数不占用类的对象的内存空间。在编译时,编译器会为每个普通成员函数生成一个独立的函数地址。类的对象通过this指针来调用这些函数,this指针作为隐含参数传递给函数,用于访问对象的成员变量。
  2. 虚拟函数:包含虚函数的类,编译器会为该类生成一个虚函数表(vtable),每个对象内部会包含一个指向这个虚函数表的指针(vptr)。虚函数表是一个函数指针数组,存放着类中虚函数的地址。当类有多个虚函数时,虚函数表中会按顺序存放这些虚函数的地址。子类继承父类的虚函数表,并根据自身重写的情况修改虚函数表中相应函数指针指向的地址。

对函数调用开销的影响

  1. 普通成员函数:调用普通成员函数时,由于函数地址在编译期就已经确定,所以直接通过函数地址进行调用,这是一种静态绑定。这种调用方式开销较小,因为不需要额外的查找过程,指令执行流程简单直接。
  2. 虚拟函数:调用虚函数时,需要通过对象的vptr指针找到虚函数表,再从虚函数表中查找对应的函数地址,然后进行函数调用,这是一种动态绑定。这个过程涉及到两次指针间接访问(先访问vptr,再从虚函数表访问函数指针),相较于普通成员函数的直接调用,增加了额外的内存访问开销,尤其是在现代CPU的缓存机制下,这种额外的内存访问可能会导致缓存不命中,从而增加指令执行时间。

对程序性能产生显著影响的场景

  1. 频繁调用场景:在循环中或者对性能要求极高的核心算法部分,如果频繁调用虚函数,由于每次调用都有额外的查找开销,累计起来会对程序性能产生显著影响。相比之下,普通成员函数由于直接调用,性能优势明显。
  2. 对象类型频繁切换场景:当在代码中频繁地在不同类型(继承体系内)的对象间切换并调用相同接口函数时,虚函数的动态绑定机制会导致每次调用都要重新查找虚函数表,开销较大。而如果使用普通成员函数,通过在调用处根据对象实际类型进行显式的类型判断和调用不同函数,可以避免虚函数的动态查找开销,提高性能。