为了实现多态,我们可以通过基类定义虚函数,派生类重写的方式实现多态。只要一个类声明了虚函数,就会为该类生成一个虚函数表。虚函数表保存了虚函数的地址。当该类实例化了,编译器就会创建这个虚函数表。并为每个对象指向该表。
虚函数表的组成
每个虚函数表包含指向类中所有虚函数的指针。每个虚函数在表中的位置是固定的,与在类中的声明顺序一致。
如果类有虚析构函数,虚函数表中会包含指向该析构函数的指针,确保在通过基类指针删除派生类对象时能正确调用析构函数。
对于包含纯虚函数的类,虚函数表中可能会有指向一个特殊的实现或标记,表示该函数是纯虚的。
基类指针指向派生类
当派生类被构造的时候,就生成了自己的虚函数表和虚指针。即使有一个基类类型的指针指向派生类对象,当调用虚函数时,通过虚指针查找的虚函数表还是派生类的虚函数表,因此执行的还是派生类的函数。
如果没有定义为虚函数,则基类指针运行成员函数一定是基类的成员函数。编译器在编译的时候就已经进行了函数地址的绑定(早绑定),而不是在需要时查询。
编译器处理虚函数表
派生类建立虚函数表三个步骤:
拷贝基类的虚函数表。多继承则拷贝每一个基类的虚函数表。
派生类和基类共用一个表,则称该基类为派生类的主基类。
查看派生类是否重写基类中的虚函数,如果有,就替换表中对应的函数为已经重写的虚函数。派生类是否有自己的虚函数,如果有,则追加自身的虚函数到自身的虚函数表中。
class Base {
public:
virtual void func1() {
cout << "Base func1" << endl;
}
virtual void func2() {
cout << "Base func2" << endl;
}
virtual ~Base() {}
};
class Derived : public Base {
public:
void func1() override {
cout << "Derived func1" << endl;
}
virtual void func3() { // 将 func3 声明为虚函数
cout << "Derived func3" << endl;
}
};
| 类名 | 虚函数表 |
|---|---|
| Base | &Base::func1 |
| &Base::func2 | |
| &Base::~Base | |
| ---------- | ----------------------------------- |
| Derived | &Derived::func1 (替换) |
| &Base::func2 | |
| &Base::~Base | |
| &Derived::func3 (追加) |
评论区