什么是虚函数
虚函数是在父类中定义的一种特殊类型的函数,允许子类重写该函数以适应其自身需求。虚函数的调用取决于对象的实际类型,而不是指针或引用类型。通过将函数声明为虚函数,可以使继承层次结构中的每个子类都能够使用其自己的实现,从而提高代码的可扩展性和灵活性。在C++中,使用关键字"virtual"来定义虚函数。
虚函数的实现机制是什么?
虚函数是通过虚函数表来实现的,虚函数表包含了一个类(所有)的虚函数的地址,在有虚函数的类对象中,它内存空间的头部会有一个虚函数表指针(虚表指针),用来管理虚函数表。当子类对象对父类虚函数进行重写的时候,虚函数表的相应虚函数地址会发生改变,当我们用一个父类的指针来操作子类对象的时候,它可以指明实际所调用的函数。
- 虚函数表:每个包含虚函数的类都会生成一个虚函数表(Virtual Table),其中存储着该类中所有虚函数的地址。虚函数表是一个由指针构成的数组,每个指针指向一个虚函数的实现代码。
- 虚函数指针:在对象的内存布局中,编译器会添加一个额外的指针,称为虚函数指针或虚表指针(Virtual Table Pointer,简称 VTable 指针)。这个指针指向该对象对应的虚函数表,从而让程序能够动态地调用正确的虚函数。
当一个基类指针或引用调用虚函数时,编译器会使用虚表指针来查找该对象对应的虚函数表,并根据函数在虚函数表中的位置来调用正确的虚函数。
虚函数的覆盖和重写
在 C++ 中,派生类可以重写 (override) 它继承的虚函数,这被称为函数的覆盖 (overriding)。当然,子类也可以选择不重写基类的虚函数,那么它将默认继承基类的实现,这就是虚函数的重载 (overloading)。
重写用关键字override(可用可不用,用了子类中必须存在与父类虚函数完全匹配的函数签名,不然编译报错)
虚函数调用是在编译时确定还是运行时确定的?
只有通过指针或者引用的方式调用虚函数是运行时确定,通过值调用的虚函数是编译期就可以确定的
https://www.zhihu.com/question/491602524/answer/2165605549
虚函数是存在类中还是类对象中(即是否共享虚表)?
答:存在类中,不同的类对象共享一张虚函数表(为了节省内存空间)。
在(基类的)构造函数和析构函数中调用虚函数会怎么样?
答:语法上没问题,但不会实现多态。
-
基类构造函数中调用虚函数
- 行为:
当基类的构造函数中调用虚函数时,只能调用基类中定义的虚函数版本,而不会调用派生类中重写的版本。 - 原因:
- 在基类的构造函数中,派生类对象还没有被完全构造(派生类部分还没有初始化)。
- 此时,
vptr
指向的是基类的虚函数表 (vtable
)。 - 即使派生类覆盖了虚函数,构造函数中也只能看到基类的虚函数表。
- 行为:
-
基类析构函数中调用虚函数
- 行为:
当基类的析构函数中调用虚函数时,只能调用基类中定义的虚函数版本,而不会调用派生类中重写的版本。 - 原因:
- 在析构函数执行时,派生类的析构函数已经被调用,派生类的成员和虚函数表已经失效。
- 此时,
vptr
已经被还原为指向基类的虚函数表。 - 所以析构函数中调用虚函数只会调用基类版本。
- 行为: