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

相关推荐
杨充3 分钟前
13.观察者模式设计思想
java·redis·观察者模式
Lizhihao_5 分钟前
JAVA-队列
java·开发语言
喵叔哟14 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟14 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk17 分钟前
maven环境搭建
java·maven
林开落L22 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色23 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧31 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices35 分钟前
C++如何调用Python脚本
开发语言·c++·python
Daniel 大东36 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea