在 C++ 中,虚函数和虚表(VTable)是实现动态多态性的关键机制。通过虚函数,可以在运行时根据实际对象的类型调用相应的函数。
虚表是一个用于存储虚函数指针的表,每个含有虚函数的类都有一个虚表。
虚函数
虚函数是一个在基类中声明的,并且在派生类中可以被重写的函数。关键字 virtual 用于声明虚函数。虚函数允许通过基类指针或引用来调用派生类的函数实现。
#include <iostream>
class Base {
public:
virtual void show() {
std::cout << "Base class show function" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class show function" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->show(); // 输出: Derived class show function
delete b;
return 0;
}
在这个例子中,Base 类中的 show 函数被声明为虚函数,Derived 类重写了该函数。当通过基类指针 b 调用 show 函数时,实际调用的是 Derived 类的 show 函数。
虚表(VTable)
每个含有虚函数的类都有一个虚表(Virtual Table),虚表中存储了指向该类虚函数实现的指针。
对于每个对象,编译器会在对象中添加一个指向虚表的指针,称为虚表指针(VPointer)。通过虚表指针,程序在运行时可以根据对象的实际类型调用相应的虚函数。
虚表的工作原理
类的虚表:编译器为每个含有虚函数的类生成一个虚表。虚表包含该类所有虚函数的指针。
对象的虚表指针:每个对象在其内存布局中包含一个指向其类的虚表的指针。
函数调用:通过基类指针调用虚函数时,程序首先通过对象的虚表指针找到虚表,然后在虚表中找到实际要调用的函数指针,最后调用该函数。
#include <iostream>
class Base {
public:
virtual void show() {
std::cout << "Base show()" << std::endl;
}
virtual void info() {
std::cout << "Base info()" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived show()" << std::endl;
}
void info() override {
std::cout << "Derived info()" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->show(); // 调用 Derived::show()
b->info(); // 调用 Derived::info()
delete b;
return 0;
}
虚表内容
在编译时,编译器会为 Base 和 Derived 类生成如下虚表:
Base 的虚表:
VTable for Base:
[0] Base::show
[1] Base::info
Derived 的虚表:
VTable for Derived:
[0] Derived::show
[1] Derived::info
对象布局
假设 Base 和 Derived 对象在内存中的布局如下:
Base 对象:
[VPTR] -> VTable for Base
Derived 对象:
[VPTR] -> VTable for Derived
调用过程
当通过基类指针 b 调用 show 和 info 函数时,程序执行以下步骤:
1.通过 b 的虚表指针找到 Derived 的虚表。
2.在虚表中找到 show 和 info 函数的指针。
3.调用相应的函数。
总结
虚函数和虚表是 C++ 实现动态多态性的关键机制。虚函数允许在运行时根据对象的实际类型调用相应的函数。虚表是存储虚函数指针的表,每个含有虚函数的类都有一个虚表,每个对象有一个指向虚表的指针。通过虚表,程序在运行时可以高效地实现多态性。