C++显式类型转换

2024-05-16 06:20
文章标签 c++ 类型转换 显式

本文主要是介绍C++显式类型转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文主要分析C++的4种显式类型转换

文章目录

  • static_cast
    • 基本数据类型转换
    • const
    • 用于类层次结构
    • void*
  • dynamic_cast
    • 继承中的转换
  • const_cast
  • reinterpret_cast
  • 参考资料


我们使用显示类型转换,就是在告诉编译器要怎么解释这块内存。

在早期C/C++中,显式的类型转换有如下两种形式

type(expr); // 函数形式的显式类型转换
(type)expr; // C语言风格 

C++的新式显式类型转换

cast-name<type>(expression);
  • cast-namestatic_castdynamic_castconst_castreinterpret_cast 四种,表示转换的方式
  • type表示转换的目标类型
  • expression 是被转换的值

下面详细讲解下这四种显式类型转换


static_cast

格式为 static_cast<type>(expression)

任何在编写程序时能够明确的类型转换都可以使用 static_cast

基本数据类型转换

基本数据类型转换,比如将int转换为double,反之亦然,需要编写程序时确认安全性

将 `int` 转换为 `double` 编译器可能的操作如下(VS2022 x64),比如有如下代码
int x = 0;
double y = static_cast<double>(x);

其汇编码为,cvtsi2sd 指令把源地址1个双字有符号整数变成1个双精度浮点数,xmm0 为英特尔处理器中的一个128位寄存器,movsd mmword ptr [y],xmm0 指令表示从 xmm0 寄存器中移动低64位到 y 地址

const

将非const对象转换为const对象 (但不能将const对象转换为非const对象,这个只有const_cast才能做到)
如果转换底层 const 对象到非 const 对象,编译器会报表达式必须是可修改的左值错误

顶层 const 也不可以被 static_cast 转换

用于类层次结构

父类和子类之间指针和引用的转换
假设我们现在有两个类,父类 A 和其派生类 B

class A {
public:virtual void foo() {std::cout << "A::foo()" << std::endl;}int m_a = 0;
};class B : public A
{
public:virtual void foo() {std::cout << "B::foo()" << std::endl;}int m_b = 0;
};
  • 进行上行转换,即把子类对象的指针/引用转换为父类指针/引用,这种转换是安全的
B b;
static_cast<A>(b).foo();
B* bptr = new B;
static_cast<A*>(bptr)->foo();

输出如下

  • 进行下行转换,即把父类对象的指针/引用转换成子类指针/引用,这种转换是不安全的,因为没有运行时检查,所以需要在编写程序时确认安全性
A* a = new A;
std::cout << static_cast<B*>(a)->m_b << std::endl; // 不安全的,访问了申请外的内存

输出如下

void*

void* 转换为目标类型的指针(这是极其不安全的),类似于上面父类指针转子类指针,可能会访问申请外的内存

void* a = new A;
std::cout << static_cast<B*>(a)->m_b << std::endl; // 不安全的,访问了申请外的内存

输出如下


dynamic_cast

格式为 dynamic_cast<type>(expression)

相比static_castdynamic_cast 会在运行时检查类型转换是否合法,具有一定的安全性。由于运行时的检查,所以会额外消耗一些性能。需要注意的是 dynamic_cast 转换仅适用于指针或引用

继承中的转换

  1. 上行转换

由子类指针/引用转换到父类指针/引用,与 static_cast 和隐式转换一样,都是安全的

B* b = new B;
A* a = dynamic_cast<A*>(b);
std::cout << a->m_a << std::endl;

可以看到下面的汇编代码中,只是把指针的值给到了 [a] 地址

  1. 下行转换

由父类指针/引用转换到子类指针/引用,使用 dynamic_cast 进行下行转换有个条件,就是其操作数必须含有多态类类型,简单理解,父类得有虚函数。

如果我们将之前 类A 和 类B 中的虚函数去掉,则会报错

如果和之前一样,类 A 中有虚函数 foo(),那么执行 dynamic_cast 会发生什么

A* a = new A;
B* b = dynamic_cast<B*>(a);
std::cout << b->m_b << std::endl;

可以看到把类 B 和类 A 的运行时类型描述符放到了 r9r8 寄存器中

之后运行时动态类型转换,这里的实现原理简单描述为,我们在虚函数表 vtbl[-1] 的位置存放着指向类型信息的 tpye_info*,那么由于我们实际指向的对象的类是 A 其取出的 RTTI Type Descriptor 应该是类 A 我们和需要转换的类 B 的 RTTI Type Descriptor 比较,然后发现不匹配,此时转换失败,会返回一个 nullptr。

如果 a 实际指向的是类 B 的对象,那么运行时转换成功。

注意转换的是引用,转换失败会抛出 bad_cast 异常


const_cast

格式为 const_cast<type>(expression)

const_cast 用于移除类型的 constvolatile_unaligned 属性

const A* const a = new A;
A* aa = const_cast<A*>(a);
aa->m_a = 2;

当然也可以用于添加 const


reinterpret_cast

格式为 reinterpret_cast<type>(expression)

非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用


参考资料

C++笔记 · C++类型转换 —— 默默默默默
C++对象模型之RTTI的实现原理 —— linux

这篇关于C++显式类型转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/994118

相关文章

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

python中各种常见文件的读写操作与类型转换详细指南

《python中各种常见文件的读写操作与类型转换详细指南》这篇文章主要为大家详细介绍了python中各种常见文件(txt,xls,csv,sql,二进制文件)的读写操作与类型转换,感兴趣的小伙伴可以跟... 目录1.文件txt读写标准用法1.1写入文件1.2读取文件2. 二进制文件读取3. 大文件读取3.1

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序