我对virtual inheritance很感兴趣,这件事给我带来了神秘。让我们考虑一个使用virtual inheritance的示例
struct Base {
virtual void v() { std::cout << "v"; }
};
struct IntermediateDerivedFirst : virtual Base {
virtual void w() { std::cout << "w"; }
};
struct IntermediateDerivedSecond : virtual Base {
virtual void x() { std::cout << "x"; }
};
struct Derived : IntermediateDerivedFirst, IntermediateDerivedSecond {
virtual void y() { std::cout << "y"; }
};最后,Derived应该如下所示:
--------
|[vtable]| -----> [ vbase offset 20 ]
|[vtable]|--- [ top offset 0 ]
|[vtable]|- | [ Derived typeinfo ]
-------- || [ IntermediateDerivedFirst::w() ]
|| [ Derived::y() ]
||
|----> [ vbase offset 12 ]
| [ top offset -8 ]
| [ Derived typeinfo ]
| [ IntermediateDerivedSecond::x()]
|
-----> [ vbase offset 0 ]
[ top offset -20 ]
[ Derived typeinfo ]
[ Base::v() ]因此,从字面上讲,virtual继承将大多数基类的vtable移到最后,我们可以看到-- vtables表示IntermediateDerivedFirst,IntermediateDerivedSecond不包含Base的v()方法的地址。好的,那么,我们可以看到这个类没有多少vtable。让我们考虑一个代码:
IntermediateDerivedFirst* fb = new Derived;
fb->v();
delete fb;这个调用仍然有效,但是,vtable for IntermediateDerivedFirst没有关于v()方法的信息,它似乎在这里使用了一些魔术,它使用第三个vtable指针来调用v()。那么,编译器如何选择所需的vtable指针来获取被调用函数的地址呢?
发布于 2021-04-29 20:11:13
Bjarne写了一篇关于使用C++:,第2卷第4期,第367至395页解决多重继承中的“钻石问题”的详细论文。
使用两个IntermediateDerived类中声明的“虚拟基础”;可以保证只获得公共基类的一个实例。
对于每个指定为虚拟的不同基类,大多数派生对象只包含该类型的一个基类子对象,即使该类多次出现在继承层次结构中(只要它每次都是虚拟继承的)。
也就是说,编译器将提供每个实例的一个实例:
这两个IntermediateDerived_X类的vtable中都有一个虚拟指针,将偏移量存储到基类。当IntermediateDerived_X类尝试访问Base::v()时,它将在其vtable中使用虚拟指针来查找Base。沿着同样的路线;如果派生继承了100个具有相同“虚拟基础”的IntermediateDerived_X类,那么仍然只会有一个Base实例。调用Base::v()函数使用虚拟指针访问基类的实例。
需要注意的是,Base对象只构造一次;当它第一次在派生类中初始化时。继承的类将从Base构造到大多数派生类。相反,在示例实例化中,破坏的顺序将从大多数派生到Base;调用‘新派生’将构造四个类中每个类的单个实例。
基于上面的地址表: IntermediateDerivedFirst具有'vbase偏移量= 20‘的知识,这是Base的位置。IntermediateDerivedSecond可以以相同的方式访问基类(VBase偏移量= 12);并且会产生完全相同的功能。没有神奇的第三个vtable,而只是指向所有派生类之间共享的Base的指针。
从实现的角度来看,在所有虚拟类中添加一个指向基类的指针通常比将虚拟表复制到每个派生类更具有内存悟性。不同的编译器、优化、链接器等可能实现或更改生成的vtable略有不同。但是,所有使用虚拟继承的派生类都只指向一个Base对象。
https://stackoverflow.com/questions/67248308
复制相似问题