深度探索C++对象模型 9.1分
读书笔记 第152页
笨蛋.mk⑨

虚拟成员函数(virtual member function)的调用 ==================================================================== 问题域: 由于不可避免的将打破与 C 的二进制兼容性, 需要仔细考虑哪些类才是需要增加虚拟函数机制的. 理论上, 这个范围可以限定在必须支持某种形式的运行期多态(runtime polymorphism)的类. runtime polymorphism 也被称为 active polymorphism, 例如:意图通过基类指针意图调用子类的 virtual function 实现. (相对的, 可在编译期被解决的则称为 passive polymorphism. 例如, 通过子类实例或指针调用的, 通过class scope operator明确调用的virtual function. 对这类调用, 均将绕开虚拟函数机制, 被当作一般的成员函数进行处理. 这也是 inline virtual function 是有意义的原因.) 实际上, 由于编译器实现上的限制, 当类中包含 virtual member function, 即会加入虚函数机制 (即使主要使用方式是 passive polymorphism. 不过这本书出现这么久, 不知道现在编译技术发展如何?). ==================================================================== 调用的实现策略:(具体方式因编译器而异) 单继承 1. 每个类均有自己的virtual function地址表格, 且只有一个. 2. 该类的每个 virtual function 均被指派一个固定表格索引值 (仅对编译器有意义). a. 每个虚函数的索引值在整个继承体系中保持不变 b. 当子类添加时 virtual function, 使用靠后的表格索引值 (在子类的 virtual function 地址表格中) c.表格中只有active virtual function, 并非全部虚函数 d.纯虚函数的地址使用pure_virtual_called()或类似函数, 一个被调用时会终止程序的函数(按实现), 担当保护者的角色 3. 在类的实例中增加指向表格的指针 ----------------------------------------------- 多重继承 子类中分别包含每个父类的virtual function table指针, 其中被子类改写的虚拟函数的地址已被重新设置. 子类实例的内存布局如下图:

Base1 subobjects
Base1 vptr
Base2 subobjects
Base2 vptr
Derived subobjects
Derived vptr (书里没写, 个人推测, 当子类具有新的虚拟成员函数时, 将出现其自身的virtual function table)

1 .当使用"第一个"父类的指针调用时, 与单继承的情况相同. 2. 当使用"第N个"父类的指针调用时 (N > 1), 将涉及两个动作 a.取得对应的virtual function table: 在子类指针转换为父类指针时, 已经按照内存布局进行了偏移计算, 于是可以正确得到virtual function table指针. 转换时的代码将类似于(假定N=2):

ptrBase2 = ptrDerived ? ptrDerived + sizeof(Base1) : 0;

b.将父类指针转换为子类指针, 作为函数参数传入: 由于父类指针是由子类指针+地址偏移得到的, 需要反向转换. 这里可以使用thunk技术实现高效转换. 3. 当使用子类指针调用时, 等于是passive polymorphism, 直接编译期解决... !!!!!!!!!!珍惜生命, 远离多重继承, 或者仅继承作为接口的基类!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 这里仅列出了多重继承问题中我所理解的部分, 实际中还有:"当Base1, Base2有相同签名的虚拟成员函数时, Base1 vptr, Base2 vptr中的地址会被如何设置?"之类的问题.

2
《深度探索C++对象模型》的全部笔记 32篇
豆瓣
免费下载 iOS / Android 版客户端