C++显式类型转换

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

文章目录


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

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

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

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

cpp 复制代码
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),比如有如下代码
cpp 复制代码
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

cpp 复制代码
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;
};
  • 进行上行转换,即把子类对象的指针/引用转换为父类指针/引用,这种转换是安全的
cpp 复制代码
B b;
static_cast<A>(b).foo();
B* bptr = new B;
static_cast<A*>(bptr)->foo();

输出如下

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

输出如下

void*

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

cpp 复制代码
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 和隐式转换一样,都是安全的

cpp 复制代码
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 会发生什么

cpp 复制代码
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 属性

cpp 复制代码
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

相关推荐
ctrigger7 分钟前
AI回答:Linux C/C++编程学习路线
linux·c语言·c++
C#Thread11 分钟前
C#上位机--循环语句
开发语言·c#
m0_7482546616 分钟前
定时任务特辑 Quartz、xxl-job、elastic-job、Cron四个定时任务框架对比,和Spring Boot集成实战
java·spring boot·后端
海边漫步者31 分钟前
Idea2024中搭建JavaFX开发环境并创建运行项目
java·intellij-idea·javafx
diemeng111937 分钟前
2024系统编程语言风云变幻:Rust持续领跑,Zig与Ada异军突起
开发语言·前端·后端·rust
Warren9841 分钟前
Springboot中分析SQL性能的两种方式
java·spring boot·后端·sql·mysql·intellij-idea
软件黑马王子1 小时前
Unity游戏制作中的C#基础(3)加减乘除算术操作符,比较运算符,逻辑与,或运算符
开发语言·unity·c#
张太行_1 小时前
Qt Creator 设计界面后的预览方法
开发语言·qt
视觉CG1 小时前
【Viewer.js】vue3封装图片查看器
开发语言·javascript·vue.js
h^hh1 小时前
洛谷 P3405 [USACO16DEC] Cities and States S(详解)c++
开发语言·数据结构·c++·算法·哈希算法