1. 虚函数底层原理深度剖析
虚函数是C++实现运行时多态的核心机制,也是大厂面试必问的底层原理题。理解虚函数的实现机制,对写出高性能、可扩展的C++代码至关重要。
1.1 虚函数表(vtable)工作机制
每个包含虚函数的类都会有一个虚函数表,这是一个在编译期生成的静态数组,存储着该类所有虚函数的函数指针。当对象被创建时,会在对象内存布局的最前面添加一个指向该虚函数表的指针(vptr)。
cpp复制class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
int base_data;
};
class Derived : public Base {
public:
void func1() override { cout << "Derived::func1" << endl; }
virtual void func3() { cout << "Derived::func3" << endl; }
int derived_data;
};
上述代码的内存布局如下:
code复制Derived对象:
+-------------------+
| vptr | -> 指向Derived类的虚函数表
+-------------------+
| base_data |
+-------------------+
| derived_data |
+-------------------+
Derived类虚函数表:
+-------------------+
| &Derived::func1 |
+-------------------+
| &Base::func2 |
+-------------------+
| &Derived::func3 |
+-------------------+
关键点:虚函数调用是通过vptr间接寻址实现的,这比普通函数调用多一次内存访问和一次指针解引用,因此会有轻微的性能开销。
1.2 虚函数调用过程详解
当通过基类指针调用虚函数时,编译器会生成如下伪代码:
cpp复制Base* ptr = new Derived();
ptr->func1();
// 实际生成的代码类似:
(*(ptr->vptr)[0])(ptr); // 0是func1在虚函数表中的索引
这个调用过程分为三步:
- 通过对象指针获取vptr
- 通过vptr找到虚函数表
- 通过函数在虚函数表中的偏移量找到实际函数地址并调用
1.3 虚函数常见面试题解析
Q1:构造函数和析构函数可以是虚函数吗?为什么?
构造函数不能是虚函数,因为:
- 构造对象时需要知道确切类型来分配内存
- 此时vptr尚未初始化,无法使用虚函数机制
析构函数通常应该是虚函数(特别是基类的析构函数),否则通过基类指针删除派生类对象时,只会调用基类的析构函数,导致派生类部分内存泄漏。
Q2:虚函数表是在什么时候创建的?
虚函数表在编译期生成,存放在程序的只读数据段(如.rodata)。每个对象的vptr在构造函数中被初始化,具体是在构造函数的最开始处由编译器插入的代码完成的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 智能指针实战应用解析
智能指针是C++11引入的重要特性,用于自动化内存管理,避免内存泄漏。大厂面试中常要求手写实现智能指针,并分析其线程安全性。
