231-C++语言级别提供的四种类型转换方式(const_cast & static_cast & reinterpret_cast & dynamic_cast)

C++语言级别提供的四种类型转换方式

C语言中,类型强转一般就是这样:

int a = (int)b;//把右边的类型强转成左边的类型,不安全,根据强转的类型造成内存大小会扩大的可能,访问不安全

C++中:

const_cast : 去掉(指针或者引用)常量属性的一个类型转换
static_cast : 提供编译器认为安全的类型转换(没有任何联系的类型之间的转换就被否定了,就编译不通过)
reinterpret_cast : 类似于C风格的强制类型转换,谈不上什么安全
dynamic_cast : 主要用在继承结构中,可以支持RTTI类型识别的上下转换

1、const_cast

去掉(指针或者引用)常量属性的一个类型转换

在这里插入图片描述
可以将const_cast看成模板。
在这里插入图片描述
底层的汇编指令是一模一样的。

但是在转换变成汇编指令之前,编译阶段,就有所不同了。

C风格的强制转换,可以随意转换,可以转为double*,没有问题!
在这里插入图片描述
编译是没有问题的。
在这里插入图片描述
但是如果是这样:使用const_cast将const int* 转换为 char*,会报错!
在这里插入图片描述

在这里插入图片描述
const_cast在进行类型强转的时候,地址a的类型是和左边的类型和<>里面的去掉常量的类型是要保持一致的(在这里就是只能转换为int*类型
在这里插入图片描述
这样const_cast才能答应强转,是语言级别的,不产生任何额外的指令代码,这两句在汇编指令上没有区别。


我们再举一个例子:
在这里插入图片描述
在这里插入图片描述
所以:const_cast<这里面必须是指针或者引用类型 int * 或 int &>
在这里插入图片描述

2、static_cast

提供编译器认为安全的类型转换(没有任何联系的类型之间的转换就被否定了,就编译不通过)

在这里插入图片描述
int和char是有关联有联系的,所以转换没问题。


但是:
在这里插入图片描述
在这里插入图片描述
这2个指针之间是没有任何联系的,就被否决了。


在这里插入图片描述
如果说让你转成功了,double类型的指针b指向了一个整型的内存,这个b一解引用,就是8个字节的内存,而实际上只有4个字节的整数内存,不安全。
在这里插入图片描述
但是,C可以!
在这里插入图片描述
在这里插入图片描述
C底层的类型强转不管你类型之间有没有联系!


在这里插入图片描述

  • 因为基类类型和派生类类型是继承结构上从上到下的类型。
  • 类型强转的时候,它们类型之间是有关系联系的。
  • static_cast可以通过它们之间的互相转换,但是转换之后,代码到底安不安全是由开发者来保证,而不是由static_cast保证。

3、reinterpret_cast

类似于C风格的强制类型转换,就是随意转了,没有任何安全可言

在这里插入图片描述
在这里插入图片描述
指针解引用8个字节,而原本内存只有4字节,造成了不安全。

4、dynamic_cast

主要用在继承结构中,可以支持RTTI类型识别的上下转换

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
上面没有问题!


但是,有可能,随着项目的进行,软件开发的需求改变了,我们Derive2要增加新的需求:
在这里插入图片描述
那么在下面showFunc这个函数,要进行判断:

如果指向的是其他派生类的对象,那么调用这个func就可以了,但是,如果指向的是Derive2这个对象,就不要调用这个func方法,应该调用derive02func这个方法。
在这里插入图片描述
我们现在的软件设计就要去实现这个功能。我们有什么办法呢?

现在就要识别*p的类型,到底指向的是哪个对象,如果是Derive2这个对象, 就要重新调用函数

1、我们可以通过typeid( * p).name() == "Derive2"比较,判断类型

  • p指针是Base类型的指针,Base里面是虚函数,识别的是运中行时的动态绑定,就是指针指向的对象,进而访问其虚函数表,取的是RTTI的类型。

2、但是我们现在是用dynamic_cast进行RTTI类型的转换

#include <iostream>
using namespace std;


class Base//抽象类 
{
public:
	virtual void func() = 0;
};
class Derive1 : public Base
{
public:
	void func() { cout << "call Derive1::func" << endl; }
};
class Derive2 : public Base
{
public:
	void func() { cout << "call Derive2::func" << endl; }
	//Derive2实现新功能的API接口函数
	void derive02func()
	{
		cout << "call Derive2::derive02func" << endl;
	}
};
/*
typeid(*p).name() == "Derive"
*/
void showFunc(Base* p)
{
	//dynamic_cast会检查p指针是否指向的是一个Derive2类型的对象?
	//通过p访问->vfptr访问->vftable RTTI信息 如果是,dynamic_cast转换类型成功,
	//返回Derive2对象的地址,给pd2;否则返回nullptr
	//static_cast是编译时期的类型转换  dynamic_cast是运行时期的类型转换,是支持RTTI信息识别的
	Derive2* pd2 = dynamic_cast<Derive2*>(p);
	if (pd2 != nullptr)
	{
		pd2->derive02func();
	}
	else
	{
		p->func();//动态绑定  *p的类型 Derive2  derive02func
	}
}
int main()
{
	Derive1 d1;
	Derive2 d2;
	showFunc(&d1);
	showFunc(&d2);

	return 0;
}

在这里插入图片描述


使用static_cast也是可以类型转换成功的,因为Base和Derive是有关联的,有继承关系,但是是永远都可以转换成功,所以无法识别出Derive2,看起来是Derive2对象了,实际上有可能是其他派生类对象,这就非常不安全了。
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liufeng2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值