虚函数表是针对类的还是针对对象的?同一个类的两个对象的虚函数表是怎么维护的?
参考回答
虚函数表(vtable)是针对类的,而不是针对对象的。
– 每个含有虚函数的类在编译时会生成一个虚函数表,表中存储该类的所有虚函数地址。
– 同一个类的所有对象共用一张虚函数表,而每个对象中包含一个隐藏的虚指针(vptr),指向类的虚函数表。
– 虚指针的初始化在对象的构造函数中完成。
详细讲解与拓展
1. 虚函数表是针对类的
- 虚函数表(vtable):
- 虚函数表是编译器为每个包含虚函数的类生成的表,用于存储该类的所有虚函数地址。
- 如果类没有虚函数,则不会生成虚函数表。
- 虚函数表是一个静态结构,编译时生成,运行时不变。
- 虚函数表的特点:
- 针对类生成:每个类只有一张虚函数表,所有对象共享。
- 存储虚函数地址:表中的每一项是一个虚函数的地址。如果子类重写了某个虚函数,虚函数表中会将该项替换为子类的实现地址。
2. 虚指针(vptr)是针对对象的
- 每个对象有一个隐藏的虚指针(
vptr),指向它所属类的虚函数表。 - 虚指针的作用:
- 在运行时,虚指针帮助对象找到其所属类的虚函数表。
- 调用虚函数时,通过虚指针访问虚函数表中的地址,完成动态绑定。
示例:
#include <iostream>
using namespace std;
class Base {
public:
virtual void func1() { cout << "Base func1" << endl; }
virtual void func2() { cout << "Base func2" << endl; }
};
class Derived : public Base {
public:
void func1() override { cout << "Derived func1" << endl; }
};
int main() {
Base baseObj;
Derived derivedObj;
Base* basePtr = &baseObj;
Base* derivedPtr = &derivedObj;
basePtr->func1(); // 输出 "Base func1"
derivedPtr->func1(); // 输出 "Derived func1"
return 0;
}
运行原理:
1. 类的虚函数表:
– Base 类的虚函数表(vtable):
“`
vtable(Base):
+——————-+
| Base::func1 |
| Base::func2 |
+——————-+
“`
– `Derived` 类的虚函数表(vtable):
“`
vtable(Derived):
+——————-+
| Derived::func1 | // 覆盖了 Base::func1
| Base::func2 | // 未覆盖,继承自 Base
+——————-+
“`
- 对象的虚指针(vptr):
baseObj的虚指针(vptr)指向Base类的虚函数表。derivedObj的虚指针(vptr)指向Derived类的虚函数表。
- 调用过程:
- 调用
basePtr->func1()时,通过baseObj的虚指针找到Base的虚函数表,调用Base::func1。 - 调用
derivedPtr->func1()时,通过derivedObj的虚指针找到Derived的虚函数表,调用Derived::func1。
- 调用
3. 同一个类的两个对象如何维护虚函数表
- 同一个类的所有对象共享同一张虚函数表。
- 每个对象有一个独立的虚指针(
vptr),指向相同的虚函数表。 - 虚指针的管理:
- 对象的构造函数负责初始化虚指针,将其指向该对象所属类的虚函数表。
- 如果对象的类层次结构发生变化(如转换为子类),虚指针会更新为指向新的虚函数表。
图示:
Base 类的虚函数表(vtable):
+-------------------+
| Base::func1 |
| Base::func2 |
+-------------------+
两个对象的虚指针(vptr):
baseObj -> vtable(Base)
baseObj2 -> vtable(Base)
- 如果
baseObj和baseObj2是Base类的两个对象,它们的虚指针都指向Base的虚函数表。
4. 子类重写虚函数时的虚函数表变化
- 当子类重写了基类的虚函数时:
- 子类的虚函数表中会将该函数的地址替换为子类的实现。
- 没有被重写的虚函数,仍然继承基类的实现。
示例:
class Base {
public:
virtual void func1() { cout << "Base func1" << endl; }
virtual void func2() { cout << "Base func2" << endl; }
};
class Derived : public Base {
public:
void func1() override { cout << "Derived func1" << endl; }
};
虚函数表:
– Base 的虚函数表:
“`
vtable(Base):
+——————-+
| Base::func1 |
| Base::func2 |
+——————-+
“`
– `Derived` 的虚函数表:
“`
vtable(Derived):
+——————-+
| Derived::func1 | // 覆盖了 Base::func1
| Base::func2 | // 继承自 Base
+——————-+
“`
总结
- 虚函数表是针对类的:每个类有一张虚函数表,存储该类的所有虚函数地址。
- 同一个类的所有对象共享虚函数表:对象通过虚指针(
vptr)访问类的虚函数表。 - 虚指针是针对对象的:每个对象有一个独立的虚指针(
vptr),指向该对象所属类的虚函数表。 - 子类重写虚函数时的表更新:子类的虚函数表会替换基类中对应虚函数的地址,而未被重写的函数仍然使用基类的实现。
通过这种机制,C++实现了动态绑定和运行时多态。