虚基表与虚继承的内存关系

虚基表与虚继承的内存关系
虚基表与虚继承的内存关系一、虚继承的核心机制虚继承通过虚基表vbtable和虚基类指针vbptr来实现确保菱形继承中只有一份虚基类副本。二、内存结构对比2.1 普通继承无虚继承的内存布局classPerson{public:string name;// 假设 32 字节64位系统intage;// 4 字节};classTeacher:publicPerson{public:string course;// 32 字节};classStudent:publicPerson{public:intstuId;// 4 字节};classTeachingAssistant:publicTeacher,publicStudent{public:string dept;// 32 字节};内存布局图解普通菱形继承的内存布局无虚继承2.2 虚继承的内存布局classPerson{public:string name;// 32 字节intage;// 4 字节};// 虚继承classTeacher:virtualpublicPerson{public:string course;// 32 字节};classStudent:virtualpublicPerson{public:intstuId;// 4 字节};classTeachingAssistant:publicTeacher,publicStudent{public:string dept;// 32 字节};虚继承内存布局图解三、虚基表vbtable的结构3.1 虚基表的内容// 虚基表的结构概念上structVirtualBaseTable{ptrdiff_t offset_to_top;// 到对象顶部的偏移ptrdiff_t offset_to_virtual_base;// 到虚基类的偏移// 可能还有多个虚基类的偏移};虚基表vbtable结构TeachingAssistant 对象的虚基表 Teacher 的虚基表 (通过 Teacher::vbptr 指向) offset_to_top 0 ← 从 Teacher 部分到对象顶部 offset_to_Person 72 ← 到 Person 部分的偏移 ↑ Teacher::vbptr ─────┘ Student 的虚基表 (通过 Student::vbptr 指向) offset_to_top -40 ← 从 Student 部分到对象顶部 offset_to_Person 32 ← 到 Person 部分的偏移 ↑ Student::vbptr ────────┘四、完整的内存偏移示例#includeiostreamusingnamespacestd;classPerson{public:string name;intage;Person():name(Person),age(0){}virtual~Person(){}};classTeacher:virtualpublicPerson{public:string course;Teacher():course(Math){}};classStudent:virtualpublicPerson{public:intstuId;Student():stuId(100){}};classTeachingAssistant:publicTeacher,publicStudent{public:string dept;TeachingAssistant():dept(CS){}};intmain(){TeachingAssistant ta;char*base(char*)ta;cout 详细地址信息 endl;cout对象起始地址: (void*)baseendl;coutTeacher 地址: (void*)(Teacher*)taendl;coutStudent 地址: (void*)(Student*)taendl;coutPerson 地址: (void*)(Person*)(Teacher*)taendl;cout\n 相对于对象起始的偏移 endl;coutTeacher 偏移: 0 字节endl;coutStudent 偏移: ((char*)(Student*)ta-base) 字节endl;coutPerson 偏移: ((char*)(Person*)(Teacher*)ta-base) 字节endl;cout\n 子对象之间的偏移 endl;coutTeacher → Person: ((char*)(Person*)(Teacher*)ta-(char*)(Teacher*)ta) 字节endl;coutStudent → Person: ((char*)(Person*)(Teacher*)ta-(char*)(Student*)ta) 字节endl;return0;}典型输出86位系统五、虚基表访问过程5.1 通过 vbptr 访问虚基类成员// 源代码ta.name张三;// 编译器转换后的伪代码// 1. 通过 Teacher::vbptr 找到虚基表// 2. 从虚基表中读取到 Person 的偏移量// 3. 计算 Person 的实际地址// 4. 访问 name 成员// 等价于ptrdiff_t offsetpTA-Teacher::vbptr-offset_to_Person;Person*pPerson(Person*)((char*)pTAoffset);pPerson-name张三;访问流程图解ta.name 张三; 步骤1获取 Teacher 部分的 vbptr vbptr ta.Teacher::vbptr (指向虚基表) 步骤2从虚基表读取偏移量 offset vbptr-offset_to_Person (例如 128 字节) 步骤3计算 Person 的实际地址 pPerson (char*)ta offset 步骤4访问成员 pPerson-name 张三六、虚继承的开销总结开销类型普通继承虚继承内存开销无额外开销每个虚基类子对象有一个 vbptr8字节时间开销直接访问通过 vbptr 间接访问多一次内存访问构造开销简单复杂需要初始化 vbptr代码复杂度简单复杂七、总结概念说明vbptr虚基类指针每个虚继承的子对象中存储指向虚基表vbtable虚基表存储虚基类相对于当前对象的偏移量偏移量从当前对象起始位置到虚基类起始位置的距离访问方式通过 vbptr → 虚基表 → 偏移量 → 计算地址 → 访问成员主要目的解决菱形继承中的数据冗余和二义性问题