885 字
4 分钟
C++面经总结-1

闲话: 原本面经都是手写积累的,今天没带平板,想想干脆也写成博客了,故开此新坑。

1. 虚函数与虚函数表#

每一个有「虚函数」的类(或者有虚函数类的派生类)都有一个虚函数表,存放着虚函数的地址(实际就是虚函数指针数组)。 注意:虚函数表由类保有,而不由类(的某个具体对象)持有。

该类的任何对象都持有一个虚函数表指针(vptr),在构造函数中生成并赋值。

💡 避坑要点: 该指针是隐式的,存放在对象内存的开头。在 64 位机上占 8 字节(32 位机占 4 字节)。这就是为什么有虚函数的类对象,其占有内存会比相同的没有虚函数的类对象大的原因。在计算内存大小时,一定要考虑内存对齐!

单继承的虚函数机制#

// 基类
class Base
{
public:
int i;
virtual void Print() { } // 虚函数
};
// 派生类
class Derived : public Base
{
public:
int n;
virtual void Print() { } // 虚函数
};

机制分析: 单继承的子类持有自己的虚函数表,该虚函数表由以下几步生成:

  1. 复制:复制父类的虚函数表。
  2. 覆盖:将子类重写过的函数,在复制的虚函数表对应地址上进行替换。
  3. 追加:在虚函数表后加上自己独有的虚函数。

运行时多态(动态多态)的本质: 多态的函数调用语句,在编译后会变成一系列指令:根据基类指针(或引用)指向的实际对象,找到该对象中存放的虚函数表指针,再顺着指针去对应的虚函数表中查找目标虚函数的地址,最后进行调用。


2. 核心 Q&A#

Q1:虚表是什么时候产生的?虚函数指针是什么时候产生的?#

  • 虚表(vtable): 是在编译器进行编译时产生的。只要编译器在编译时发现一个类声明了 virtual 函数,就会为这个类创建一个虚表。通常存放在只读数据段(.rodata / .rdata)。
  • 虚函数指针(vptr): 是在创建对象时产生的,具体是在调用构造函数时进行初始化赋值的。

Q2:构造函数和析构函数可以是虚函数吗?#

结论:构造函数不可以是虚函数,析构函数通常需要是虚函数(当作为基类时)。

  • 为什么构造函数不能是虚函数? 在生成子类对象时,会先调用父类构造函数。这时虚指针先指向父类的虚函数表,再调子类的构造函数进行赋值。如果构造函数本身是虚函数,要想调用它就必须通过虚指针查表找地址。但此时对象还没实例化完成,虚函数指针也没有指向正确的子类虚表,这就陷入了逻辑冲突,因此无法实现多态。
  • 为什么析构函数要是虚函数? 当一个类作为基类时,其析构函数通常必须被声明为虚函数。如果父类析构函数没有声明为虚函数,在使用基类指针 delete 指向的子类对象时,只会静态绑定调用父类的析构函数,从而发生局部销毁。这会导致子类独有的成员无法被销毁,引发内存泄漏。
C++面经总结-1
https://ochagama.xyz/posts/cppstudy-1/
作者
海岬的人
发布于
2026-01-14
许可协议
CC-BY-NC-SA 4.0
封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00