找回密码
 立即注册
首页 业界区 业界 虚函数表里有什么?(二)——普通单继承下的虚函数表 ...

虚函数表里有什么?(二)——普通单继承下的虚函数表

掳诚 2025-6-2 00:35:39
前言

上篇文章中,我们探索了单个多态对象(没有继承)的虚函数表中的条目及它们的作用。本文继续探究普通单继承下的虚函数表。
本节示例代码如下:
  1. 1 #include <iostream>
  2. 2 #include <typeinfo>
  3. 3
  4. 4 class Base
  5. 5 {
  6. 6 public:
  7. 7     Base() {}
  8. 8     virtual ~Base() {}
  9. 9     virtual void zoo()
  10. 10     {
  11. 11         std::cout << "Base::zoo\n";
  12. 12     }
  13. 13     virtual void foo() = 0;
  14. 14 private:
  15. 15     int b_num = 100;
  16. 16 };
  17. 17
  18. 18 class Derived : public Base
  19. 19 {
  20. 20 public:
  21. 21     Derived() {}
  22. 22     ~Derived() {}
  23. 23     virtual void fun()
  24. 24     {
  25. 25         std::cout << "Derived::fun\n";
  26. 26     }
  27. 27     void foo() override
  28. 28     {
  29. 29         std::cout << "my num is: " << d_num << '\n';
  30. 30     }
  31. 31 private:
  32. 32     int d_num = 200;
  33. 33 };
  34. 34
  35. 35 int main(int argc, char *argv[])
  36. 36 {
  37. 37     std::cout <<sizeof(Derived) << '\n';
  38. 38     Base *p = new Derived;
  39. 39     const std::type_info &info = typeid(*p);
  40. 40     std::cout << info.name() << '\n';
  41. 41     delete p;
  42. 42     return 0;
  43. 43 }
复制代码
因为过分深入细节会偏离主题,所以本文仅点到为止,等到讲述完虚函数表相关的内容,后面会专门拿出一篇文章,结合实例,讲解 __dynamic_cast 的实现细节,帮助读者把之前的知识融会贯通。
gcc源码位置:__dynamic_cast、__si_class_type_info::__do_dynamic。
用途三:dynamic_cast中寻找public基类

如果通过找到了转换目标的地址,但是却不能确定 src_type 是不是 dst_type 的public基类(如果不是,转换就会失败,返回空指针),因此需要从 dst_type 向上回溯,看能不能找出到 src_type 的public路径。
  1. try {
  2.     throw Derived();
  3. } catch (const Base& b) {
  4.     b.foo();
  5. }
复制代码
 
 __find_public_src 是 __class_type_info 的成员函数,在tinfo.h中定义。
  1. bool __class_type_info::
  2. __do_catch (const type_info *thr_type,
  3.             void **thr_obj,
  4.             unsigned outer) const
  5. {
  6.   // 这里==调用的是基类std::type_info的operator==, 本质上就是比较typeinfo name这一字符串常量
  7.   if (*this == *thr_type)
  8.     return true;
  9.   if (outer >= 4)
  10.     // Neither `A' nor `A *'.
  11.     return false;
  12.   // 如果不匹配,就看thr_type的上层类型是否匹配
  13.   return thr_type->__do_upcast (this, thr_obj);
  14. }
复制代码
__si_class_type_info::__do_find_public_src 会逐级向上回溯。
  1. bool __si_class_type_info::
  2. __do_upcast (const __class_type_info *dst, const void *obj_ptr,
  3.              __upcast_result &__restrict result) const
  4. {
  5.   // 如果当前类型和dst(即要捕获的类型)相同,返回true
  6.   if (__class_type_info::__do_upcast (dst, obj_ptr, result))
  7.     return true;
  8.   // 否则看基类类型是否和dst相同
  9.   return __base_type->__do_upcast (dst, obj_ptr, result);
  10. }
  11. bool __class_type_info::
  12. __do_upcast (const __class_type_info *dst, const void *obj,
  13.              __upcast_result &__restrict result) const
  14. {
  15.   if (*this == *dst) // 相同就返回true
  16.     {
  17.       result.dst_ptr = obj;
  18.       result.base_type = nonvirtual_base_type;
  19.       result.part2dst = __contained_public;
  20.       return true;
  21.     }
  22.   return false;
  23. }
复制代码
那么,什么情况下需要逐级寻找public base呢?比如说下面的代码:
  1. Derived *pd = new Derived;
  2. Basel *pb = dynamic_cast<Base *>(pd);
复制代码
因为这里继承关系比较复杂(涉及到虚拟继承),所以 __do_dyncast 不能确定 dst2src 是什么,需要再次回溯。由于虚拟继承超出了本文的讨论范围,因此暂不深入分析,留待后序文章探讨。
总结


  • 在vtable中,纯虚函数对应 __cxa_pure_virtual 这个错误处理函数,该函数的本质是调用 about() ,即,如果调用纯虚函数,会导致程序奔溃。
  • 如果一个类只有一个基类,并且这个基类是public的、非虚的、多态的(含有虚函数),那么,派生类对象和基类子对象公用一个vtable,对于某个条目,如果派生类有自己的实现,那么就采用派生类的版本,否则,采用基类的版本。对于派生类新增的虚函数,按声明顺序依次排在最后面。
  • 对于满足上述条件的派生类,它对应的typeinfo类型是 __si_class_type_info ,该类是 __class_type_info 的派生类,含有一个指向基类typeinfo的指针 __base_type ,依靠该指针,可以从派生类类型到基类类型进行逐层回溯,这在异常捕获、 dynamic_cast 中发挥着重要作用。
由于在下才疏学浅,能力有限,错误疏漏之处在所难免,恳请广大读者批评指正,您的批评是在下前进的不竭动力。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册