虚函数表里有什么?(二)——普通单继承下的虚函数表
前言上篇文章中,我们探索了单个多态对象(没有继承)的虚函数表中的条目及它们的作用。本文继续探究普通单继承下的虚函数表。
本节示例代码如下:
1 #include <iostream>
2 #include <typeinfo>
3
4 class Base
5 {
6 public:
7 Base() {}
8 virtual ~Base() {}
9 virtual void zoo()
10 {
11 std::cout << "Base::zoo\n";
12 }
13 virtual void foo() = 0;
14 private:
15 int b_num = 100;
16 };
17
18 class Derived : public Base
19 {
20 public:
21 Derived() {}
22 ~Derived() {}
23 virtual void fun()
24 {
25 std::cout << "Derived::fun\n";
26 }
27 void foo() override
28 {
29 std::cout << "my num is: " << d_num << '\n';
30 }
31 private:
32 int d_num = 200;
33 };
34
35 int main(int argc, char *argv[])
36 {
37 std::cout <<sizeof(Derived) << '\n';
38 Base *p = new Derived;
39 const std::type_info &info = typeid(*p);
40 std::cout << info.name() << '\n';
41 delete p;
42 return 0;
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路径。
try {
throw Derived();
} catch (const Base& b) {
b.foo();
}
__find_public_src 是 __class_type_info 的成员函数,在tinfo.h中定义。
bool __class_type_info::
__do_catch (const type_info *thr_type,
void **thr_obj,
unsigned outer) const
{
// 这里==调用的是基类std::type_info的operator==, 本质上就是比较typeinfo name这一字符串常量
if (*this == *thr_type)
return true;
if (outer >= 4)
// Neither `A' nor `A *'.
return false;
// 如果不匹配,就看thr_type的上层类型是否匹配
return thr_type->__do_upcast (this, thr_obj);
}__si_class_type_info::__do_find_public_src 会逐级向上回溯。
bool __si_class_type_info::
__do_upcast (const __class_type_info *dst, const void *obj_ptr,
__upcast_result &__restrict result) const
{
// 如果当前类型和dst(即要捕获的类型)相同,返回true
if (__class_type_info::__do_upcast (dst, obj_ptr, result))
return true;
// 否则看基类类型是否和dst相同
return __base_type->__do_upcast (dst, obj_ptr, result);
}
bool __class_type_info::
__do_upcast (const __class_type_info *dst, const void *obj,
__upcast_result &__restrict result) const
{
if (*this == *dst) // 相同就返回true
{
result.dst_ptr = obj;
result.base_type = nonvirtual_base_type;
result.part2dst = __contained_public;
return true;
}
return false;
}那么,什么情况下需要逐级寻找public base呢?比如说下面的代码:
Derived *pd = new Derived;
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 中发挥着重要作用。
由于在下才疏学浅,能力有限,错误疏漏之处在所难免,恳请广大读者批评指正,您的批评是在下前进的不竭动力。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]