C++ libcxxabi中dynamic_cast 实现

  摘要:最近在看一个崩溃的过程中详细看了一遍cxxabi的定义,就想着看一些llvm中cxxabi的一些实现。本文描述了cxxabi中dynamic_cast的实现以及原理。
  关键字cxxabi,dynamic_cast

1 简介

  C++中,dynamic_cast用于有虚函数的继承链中父类型到子类型的安全转换。比较常见的用法如下:

class A{
public:
    virtual ~A() = default;
};

class B{
};

A *p = new B;
B* pp = dynamic_cast<B*>(p);

  dynamic_cast如何识别当前类的类型,这依赖于RTTI。C++中包含虚函数的对象都有一个虚函数表,一般情况下都在首地址(多继承和虚继承会有多个)有一个指向该虚函数表的虚函数表指针。虚函数表中有以下内容:

  • 基类偏移;
  • typeinfo;
  • 如果有虚函数的话会有虚析构函数指针,一般情况下有两个;

The entries for virtual destructors are actually pairs of entries. The first destructor, called the complete object destructor, performs the destruction without calling delete() on the object. The second destructor, called the deleting destructor, calls delete() after destroying the object. Both destroy any virtual bases; a separate, non-virtual function, called the base object destructor, performs destruction of the object but not its virtual base subobjects, and does not call delete().

  • 虚函数指针,如果是虚继承对应的虚函数指针可能是一个thunk function。

A segment of code associated (in this ABI) with a target function, which is called instead of the target function for the purpose of modifying parameters (e.g. this) or other parts of the environment before transferring control to the target function, and possibly making further modifications after its return. A thunk may contain as little as an instruction to be executed prior to falling through to an immediately following target function, or it may be a full function with its own stack frame that does a full call to the target function.

  C++中就是通过虚函数表携带的typeinfo信息来确认当前类的类型,如果是该类型就就可以转换成功,否则的话就会转换失败。cxxabi的基本实现思路也是如此。

2 cxxabi实现

  先来看一下cxxabi中对应实现函数的声明。

  • static_ptr:期望将进行转换的类地址;
  • static_type:当前类的类型信息;
  • dst_type:目标转换类的类型信息;
  • src2dst_offset:由 Itanium ABI 所规定的 hint 值,辅助优化用:
    • 是一个非负整数值,说明From是To的唯一一个公开非虚基类,且From基类子对象在一个To对象中的偏移为src2dst_offset;
    • -1表示无hint;
    • -2表示From不是To的公开基类;
    • -3表示To存在多个公开From基类,但是这些公开From基类都不是To的虚基类。
extern "C" _LIBCXXABI_FUNC_VIS void *
__dynamic_cast(const void *static_ptr, const __class_type_info *static_type, const __class_type_info *dst_type, std::ptrdiff_t src2dst_offset) 

在这里插入图片描述

  首先是从虚函数表中提取出当前类对象相对于子类首地址的offset和typeinfo,这两项都是固定存储在虚函数表指针所指向位置的-1和-2位置处。

The offset to top holds the displacement to the top of the object from the location within the object of the virtual table pointer that addresses this virtual table, as a ptrdiff_t. It is always present. The offset provides a way to find the top of the object from any base subobject with a virtual table pointer. This is necessary for dynamic_cast<void*> in particular.

    void **vtable = *static_cast<void ** const *>(static_ptr);
    ptrdiff_t offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);
    const void* dynamic_ptr = static_cast<const char*>(static_ptr) + offset_to_derived;
    const __class_type_info* dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);

  接下来便是根据期望转换的目标对象的typeinfo和当前获取的typeinfo作对比进而选择是否将进行更进一步的转换。需要注意的是比较两个对象是否相同的方式:

static inline bool is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp){
    // Use std::type_info's default comparison unless we've explicitly asked
    // for strcmp.
    if (!use_strcmp)
        return *x == *y;
    // Still allow pointer equality to short circut.
    return x == y || strcmp(x->name(), y->name()) == 0;
}

相同类型
  typeinfo是编译器生成的,是静态对象用户只能读写,这里提供了使用typeinfo的名字和指针来比较的方式,是为了避免一些场景下typeinfo不一致导致转换失败(虽然ABI中默认是关闭的)。
  针对typeinfo相同的情况下则需要根据src2dst_offset来辅助优化:

  • src2dst_offset为非负整数时,fromt是to的唯一公开非虚基类。但是to类型是有可能有其他非公开基类的,因此需要比较vtptr中的偏移和src2dst_offset,能够匹配则直接返回偏移后的地址,否则返回空指针;
  • src2dst_offset为-2表示,from不是to的公开基类,直接返回空指针;
        if (src2dst_offset >= 0){
            // The static type is a unique public non-virtual base type of
            //   dst_type at offset `src2dst_offset` from the origin of dst.
            // Note that there might be other non-public static_type bases. The
            //   hint only guarantees that the public base is non-virtual and
            //   unique. So we have to check whether static_ptr points to that
            //   unique public base sub-object.
            if (offset_to_derived == -src2dst_offset)
                dst_ptr = dynamic_ptr;
        }else if (src2dst_offset == -2){
            // static_type is not a public base of dst_type.
            dst_ptr = nullptr;
        }
  • src2dst_offset无法提供帮助时,只能所搜继承图来确认是否存在继承关系。此时只能搜索整张继承图,搜索出从最派生对象所有的from公开基类子对象。如果from所指向的对象是这些from基类子对象中的某一个,那么转换成功,转换结果是最派生对象指针;否则转换失败。在搜索继承图的过程中可以应用一些剪枝方法降低搜索开销。例如一旦搜索到from指向的对象就停止搜索、不搜索包含私有继承的路径等。
info.number_of_dst_type = 1;
// Do the  search
dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, false);

不相同类型
  typeinfo不相同时,表示源和目标只是在相同的继承图上,但是并不存在直接的公开继承关系,因此为了确认是否真的存在该关系只能搜索当前源和目标的继承图来确认。

libcxxabi的dynamic_cast实现似乎有性能问题https://reviews.llvm.org/D137315#3910662这个patch修复了该问题。修复完的benchmarkhttps://gist.github.com/Lancern/212a26a3144343f459428dffe202cde0

搜索策略
  对于不同继承类型的类的 type info 有不同的搜索策略。例如对于有虚多继承的类的 type info(__vmi_class_type_info)、单继承的类的 type info(__si_class_info)等。搜索的方式看起来就是广度优先搜索再加上一些剪枝的优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/190988.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

监控同一局域网内其它主机上网访问信息

1.先取得网关IP 2.安装IPTABLES路由表 sudo apt-get install iptables 3.启用IP转发 sudo sysctl -p 查看配置是否生效 4.配置路由 iptables -t nat -A POSTROUTING -j MASQUERADE 配置成功后,使用sudo iptables-save查看

Android获取原始图片Bitmap的宽高大小尺寸,Kotlin

Android获取原始图片Bitmap的宽高大小尺寸&#xff0c;Kotlin val options BitmapFactory.Options()options.inJustDecodeBounds trueval decodeBmp BitmapFactory.decodeResource(resources, R.mipmap.p1, options)//此时&#xff0c;decode出来的decodeBmp宽高并不是原始图…

Linux 中的 ls 命令使用教程

目录 前言 如何运用 ls 命令 1、列出带有所有权的文件和目录 2、获取以人类可读的方式显示的信息 3、列出隐藏文件 4、递归列出文件 5、在使用 ls 时对文件和目录做区分 6、列出指定扩展名的文件 7、基于大小对输出内容排序 8、根据日期和时间排序文件 让我们来总结…

【PyQt】(自定义类)阴影遮罩-升级版

这是之前发的代码(自定义类)阴影遮罩的升级版。 升级就升级在&#xff0c;优化了对非矩形控件的遮盖效果&#xff0c;例如圆角按钮&#xff0c;以及默认方法不满足时可以传入其他的遮盖方法。 自定义阴影遮罩Mask&#xff1a; class Mask(QWidget):__excludeNone__colorNonecl…

【中间件】消息队列中间件intro

中间件middleware 内容管理 introwhy use MQMQ实现漫谈主流消息队列QMQ IntroQMQ架构QMQ 存储模型 本文还是从理论层面分析消息队列中间件 cfeng现在处于理论分析阶段&#xff0c;以中间件例子&#xff0c;之前的blog对于中间件是从使用角度分享了相关的用法&#xff0c;现在就…

带你用uniapp从零开发一个仿小米商场_9. 轮播图组件封装及使用

导航栏有了,接下来就是轮播图了,轮播图如下, 因为uniapp 官方自己有轮播图,所以这里就不自己写了,直接使用uniapp的轮播图二次开发就好 uniapp的轮播图组件叫swiper ,感兴趣的朋友可以点击链接,直接去看官方文档,也可以看我这里实操 用hbuilderX编译uniapp的代码有一个好处…

FO-like Transformation

参考文献&#xff1a; [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

枚举的第一行

2023年11月26日 问题: 好奇enum的所声明的枚举类的第一行是什么 从java技术卷1中第五章5.6中,了解是枚举类的实例 验证 错误信息: 解释: 此时只有有参构造 在这个枚举类里不能使用空,大概意思是说不能使用空参创建实例 校验 在原有的基础上创建一个无参构造 结果:不再报错,第…

常见树种(贵州省):019滇白珠、杜茎山、苍山越桔、黄背越桔、贵州毛柃、半齿柃、钝叶柃、细枝柃、细齿叶柃木、土蜜树、山矾、胡颓子、檵木

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、滇白珠…

电路 buck-boost相关知识

BUCK-BOOST 文章目录 BUCK-BOOST前言一、DC-DC工作模式电容电感特性伏秒积平衡原理 二、BUCK电路三、BOOST电路四、BUCK-BOOST电路总结 前言 最近需要用到buck-boost相关的电路知识&#xff0c;于是便写下这篇文章复习一下。 一、DC-DC 在学习buck-boost电路之前我们先来看一…

steam/csgo搬砖项目真能月入过万吗?到底真的假的

steam/csgo搬砖第三课之如何出售 steam搬砖核心原理是什么&#xff1f;为什么会有差价产生&#xff1f;buff不是更低价吗&#xff1f;很多小白会有这些疑问&#xff01; steam搬砖指的是通过买卖csgo游戏装备赚钱的。 玩过游戏的应该就很清楚&#xff0c;像绝地求生&#xff…

新的centos7.9安装jenkins(二)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 接上一节文章。 这个版本默认git也安装好了&#xff0c;所以全局配置这个不需要了。 maven安装3.9.3版本…

第一百八十一回 如何绘制阴影效果

文章目录 1. 概念介绍2. 使用方法2.1 SegmentedButton2.2 ButtonSegment 3. 代码与效果3.1 示例代码3.2 运行效果 4. 内容总结 1. 概念介绍 我们在本章回中介绍的SegmentedButton组件是一种分段式按钮&#xff0c;它把多个按钮连接成一组显示&#xff0c;组内再对不同的按钮进…

231126 刷题日报

1. 高楼扔鸡蛋 O(N*logN) 2. 698. 划分为k个相等的子集 没做出来&#xff0c;和划分两个子集不同 3. 300. 最长递增子序列 LIS petencie sorting 没看懂&#xff0c;明天看吧 4. 518. 零钱兑换 II 完全背包问题&#xff1a;每个物品数量是无限的 注意&#xff1a;dp的定义…

STM32F103C8T6_PWM引脚

可以看到&#xff1a;一共可以产生4 x 416路PWM信号&#xff1a;每个TIMER4路PWM&#xff0c; PA0,PA1,PA2,PA3,PA8,PA10,PA11; PA共7个 PB0,PB1,PB6,PB7,PB8,PB9,PB14; PB共7个

PTA NeuDS-数据库题目集

一.判断题 1.在数据库中产生数据不一致的根本原因是冗余。T 解析&#xff1a;数据冗余是数据库中产生数据不一致的根本原因&#xff0c;因为当同一数据存储在多个位置时&#xff0c;如果其中一个位置的数据被修改&#xff0c;其他位置的数据就不一致了。因此&#xff0c;在数据…

Jmeter接口测试快速入门 以飞致云平台为例

接口测试快速入门 以飞致云平台为例-CSDN博客 飞致云电商平台可以做接口测试练习。快速了解如何测试接口&#xff0c;如何做关联 系统基地址&#xff1a;https://gz.fit2cloud.com/ 接口测试快速入门 以飞致云平台为例-CSDN博客 博文中介绍了如何在swagger页面上进行接口测试。…

文本转语音:微软语音合成标记语言 (SSML) 文本结构和事件

​ SSML 的语音服务实现基于万维网联合会的语音合成标记语言版本 1.0。 ​ 语音服务支持的元素可能与 W3C 标准不同。 每个 SSML 文档是使用 SSML 元素&#xff08;或标记&#xff09;创建的。 这些元素用于调整语音、风格、音节、韵律、音量等。 下面是 SSML 文档的基本结构…

基本数据结构二叉树(1)

目录 1.树概念及结构 1.1树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树&#xff1a; 2.3 特殊的二叉树&#xff1a; 2.5 二叉树的存储结构 2. 链式存…

事件委派+自定义属性+编程式导航实现路由跳转及传参

当我们页面中有许多a标签需要实现点击跳转到同一个页面并携带不同的参数时&#xff0c;我们就可以使用事件委派自定义属性编程式导航 的方式&#xff0c;用最小的内存实现路由跳转的最大效率。 为什么我们不用router-link 进行跳转&#xff1f; 要知道&#xff0c;我们页面中…