1. 虚函数的概念与基本特性
虚函数是C++面向对象编程中实现运行时多态的核心机制。简单来说,虚函数允许我们在基类中声明一个函数,然后在派生类中重新定义这个函数的具体实现。当通过基类指针或引用调用这个函数时,实际执行的是对象所属派生类的版本。
虚函数的声明方式是在函数声明前加上virtual关键字:
cpp复制class Base {
public:
virtual void show() { // 虚函数声明
cout << "Base class show" << endl;
}
};
虚函数有几个关键特性值得注意:
-
动态绑定:虚函数的调用是在运行时根据对象的实际类型决定的,而不是在编译时根据指针或引用的类型决定。这是实现多态的基础。
-
虚函数表:编译器会为每个包含虚函数的类生成一个虚函数表(vtable),其中存储了指向该类虚函数实现的指针。每个对象则包含一个指向相应虚函数表的指针(vptr)。
-
继承性:一旦一个函数在基类中被声明为虚函数,它在所有派生类中自动成为虚函数,即使没有显式使用
virtual关键字。
提示:虽然派生类中可以省略virtual关键字,但显式写上可以提高代码可读性,明确表达设计意图。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 虚函数的工作原理与底层实现
理解虚函数的底层实现机制对于高效使用它们至关重要。让我们深入探讨虚函数表(vtable)和虚指针(vptr)的工作方式。
2.1 虚函数表的结构
每个包含虚函数的类都有一个对应的虚函数表,这是一个编译器生成的静态数组,存储着该类所有虚函数的地址。例如:
cpp复制class Shape {
public:
virtual void draw() = 0;
virtual void resize() = 0;
virtual ~Shape() {}
};
对应的虚函数表大致如下:
code复制Shape的vtable:
[0]: draw()的地址
[1]: resize()的地址
[2]: ~Shape()的地址
2.2 对象内存布局与虚指针
每个包含虚函数的类的对象在内存中都有一个隐藏的虚指针(vptr),通常位于对象布局的最前面。这个指针指向该类的虚函数表。例如:
cpp复制class Circle : public Shape {
public:
void draw() override { /*...*/ }
void resize() override { /*...*/ }
};
Circle c;
对象c的内存布局大致如下:
code复制+----------------+
| vptr | --> 指向Circle的vtable
| Circle的数据成员 |
+----------------+
2.3 虚函数调用过程
当通过基类指针调用虚函数时:
- 通过对象的vptr找到对应的vtable
- 在vtable中找到相应函数的地址
- 跳转到该地址执行函数
这个过程完全是在运行时完成的,因此称为"动态绑定"或"晚期绑定"。
3. 纯虚函数与抽象类
3.1 纯虚函数的定义与特性
纯虚函数是一种特殊的虚函数,它在基类中没有实现,强制派生类必须提供实现。声明纯虚函数的语法是在函数声明后加上= 0:
cpp复制class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
纯虚函数有几个重要特点:
-
强制实现:包含纯虚函数的类不能实例化,派生类必须实现所有纯虚函数才能实例化。
-
接口定义:纯虚函数常用于定
