953 字
5 分钟
12虚函数表的内存管理
虚函数表的内存管理
class Base{private: int data;
public: virtual void f1(); virtual void f2(); virtual void f3();};
class Derived{private: char* name;public: virtual void f1() {std::cout << "derived f1()" << std::endl } // 重载 virtual void g1(); virtual void h1();};
类的内存结构
类的内存结构如下:
- 虚函数表地址
- 各个成员变量
- …(成员变量)
例如上面代码中,Base对象的内存大小为16,主要包括:
虚函数表指针
,8Bytesint数据
,4Bytes内存对齐
,4Bytes
虚函数表的结构
成员函数并不直接存储在类所属的内存中,而是单独开辟一块内存空间(叫做虚函数表)存储。
虚函数表中存储的函数,以函数指针的形式存在。需要对函数指针解引用,才能调用函数。
多态和虚函数表
每个类都有自己的虚函数表,Derived类继承自Base类后,会给自己开辟一块虚函数表空间,存储自己的成员函数。
Base类有自己的虚函数表,内部存储成员函数的函数指针。
Derived类的虚函数表中,存在三种函数,对应三种指针。
- 被重写的函数,指向重写函数。
- 继承自Base的函数,指向基类的函数。
- 类自己的成员函数,指向自己的成员函数。
多态
Derived d();Base* bp = &d;bp->f1(); // 调用Derived的版本
bp是指向Derived对象的指针,同时指向了Derived类的虚函数表。
当调用f1()
函数时,根据bp找到Derived的虚函数表,从而在虚函数表中寻找f1()
函数对应的指针。
因为Derived虚函数表中的函数指针指向重写后的版本,因此调用的是重写后的f1()
函数
多重继承下的多态
在多重继承下,派生类会保存多个虚函数表,每个虚函数表对应一个基类。
并且,派生类未重写的成员函数会默认存储在第一个Vtable
中。
虚继承和菱形继承
如果存在菱形继承情况,类A中的成员变量/函数会在D中重复声明,存在二义性问题。
此时就需要使用虚继承来解决。
class A {public: virtual void func() { cout << "A::func" << endl; } virtual ~A() = default;};
class B : public virtual A {};class C : public virtual A {};
class D : public B, public C {public: void func() override { cout << "D::func" << endl; }};
int main() { D d; A* p = &d; p->func(); // 调用 D::func()}
在虚继承情况下,B
和C
共享A
的唯一实例,所有的基类共享一个vtable
,因此即使使用多态,仍然访问D::func()
协变返回类型
协变返回类型(Covariant Return Type),属于函数重写相关的概念,当一个虚函数的派生类重写基类的虚函数时,使用协变返回类型能够使派生类重写的函数,返回更具体地子类类型。
#include <iostream>using namespace std;
class Parent {public: virtual Parent* getInstance() { return new Parent(); }};
class Child : public Parent {public: Child* getInstance() override { // 返回更具体的子类类型 return new Child(); }};
int main() { Child c; Child* newObj = c.getInstance(); // 直接获得 Child 类型,无需转换 delete newObj; return 0;}
如果不满足虚函数的情况,那么当使用基类指针调用函数时,返回子类版本的函数会被隐藏,导致协变返回类型失效。
#include <iostream>using namespace std;
class Parent {public: Parent* getInstance() { // ❌ 不是 virtual 函数 return new Parent(); }};
class Child : public Parent {public: Child* getInstance() { // ❌ 不是 override,而是隐藏 return new Child(); }};
int main() { Parent* p = new Child(); Parent* obj = p->getInstance(); // 调用的是 Parent::getInstance() cout << "Type: " << typeid(*obj).name() << endl; // 仍然是 Parent,而不是 Child delete obj; delete p; return 0;}