C++语言级别四种类型转换

C语言中的强制类型转换通常不安全,根据强转的类型造成变量内存扩大或缩小,访问不安全

  • const_cast : 去掉const属性的一个类型转换,只能操作指针或引用类型,转换前后指针访问的大小不一样,就禁止转换
  • static_cast : 提供编译器认为安全的类型转换(没有任何联系的类型之间的转换无法通过编译),可以将Derive转为BaseBase不能转为Derive),Base*Derive*可以互相转换,可以操作指针、引用以及对象
  • reinterpret_cast : 类似于C风格的强制类型转换,谈不上什么安全,操作指针类型都能成功只能操作指针和引用
  • dynamic_cast : 主要用在继承结构中,可以支持RTTI类型识别的上下转换。基类指针转成相应的派生类对象指针的时候,会识别该指针是否能够进行转换,也是只能操作指针和引用

一、const_cast

首先写一段代码,将const int*转换为int*
在这里插入图片描述
我们来看一下汇编指令

在这里插入图片描述
可以看到,由于const_cast是语言级别的,不产生任何额外的指令代码,这两句在汇编指令上没有区别,两行代码转换的汇编指令是完全一样的,但是在编译阶段会有所差别

我们再看,这时我们使用类型强转将const int*转换为double*,p1指向的内存空间是const int a的空间,但是p1解引用后访问的范围是4字节,这就不安全了,然而编译依然通过

在这里插入图片描述
在这里插入图片描述
我们使用const_cast将const int*转换为char*试试

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

这就不被允许了,因为指针解引用后访问的范围和指针指向的变量不一致,这会不安全

在这里插入图片描述

在这里插入图片描述
只要是指针访问的范围和实际指向的变量空间不一致,const_cast就不允许转换

再举一个例子:

在这里插入图片描述
在这里插入图片描述
const_cast的参数必须是指针或引用类型

二、static_cast

static_cast静态类型转换应该是我们用的最多的,提供编译器认为安全的类型转换,没有任何联系的类型之间的转换就被否定了,编译不通过

	int num = 100;
	char c1 = (char)num;                 // 字符d
	char c2 = static_cast<char>(num);    // 字符d

这样转换是没有问题的,因为int和char是有联系的,在ASCII码表中100对应字符d

我们试试将int*转换为double*

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

编译不通过,因为int*double*是没有任何联系的,所以static_cast不允许转换。如果static_cast允许转换了,指针访问的范围就变了,用指向8字节空间的指针访问4字节内存,会导致内存访问不安全

在这里插入图片描述
而我们用C风格的写法,是可以编译通过的

此外,static_cast还支持基类和派生类类型的转换,因为基类类型和派生类类型是继承结构上从上到下的类型,它们类型之间是有关系联系的

static_cast可以通过它们之间的互相转换,但是转换之后,代码到底安不安全是由开发者来保证,而不是由static_cast保证。

使用static_cast:

  1. 有联系的类型之间可以互相转换
  2. 没有任何联系的类型之间无法转换
  3. 基类类型与派生类类型进行转换,可以用static_cast,它们类型之间有关系,但不一定安全

三、reinterpret_cast

类似于C风格的强制类型转换,和内存访问安全就没有什么关系了
在这里插入图片描述
编译通过,但使用起来不安全

四、dynamic_cast

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; }
};

void showFunc(Base* p) {
	p->func();  // 动态绑定
}

int main(){
	Derive1 d1;
	Derive2 d2;
	showFunc(&d1);
	showFunc(&d2);
	return 0;
}

Base里面是虚函数,是运行时的动态绑定,取的是RTTI的类型,就是指针指向的对象,进而访问其虚函数表,执行派生类的同名覆盖方法
在这里插入图片描述
但是随着项目的进行,软件开发的需求改变了,我们要增加新的需求:如果Base指针指向其他的Derive对象,就调用func方法,如果指向Derive2对象,就调用new_func方法

class Derive2 : public Base {
public:
	void func() { cout << "call Derive2::func " << endl; }
	void new_func() { cout << "call Derive2::new_func " << endl; }
};

如果现在的软件设计就要去实现这个功能,我们应该怎么做?

现在需要识别*p的类型,到底指向的是哪个对象,如果是Derive2的对象, 就要调用new_func方法

我们可以通过typeid(*p).name() == "Derive2"比较,判断p指向的对象,进而调用不同的函数,dynamic_cast就是通过这种方式进行RTTI类型的转换

void showFunc(Base* p) {
	// 访问指针p指向的对象,访问对象内存中的vfptr,进而访问vftable,获取RTTI信息
	// 如果RTTI类型是Derive2,就返回Derive2对象的地址,否则返回nullptr
	Derive2* pd2 = dynamic_cast<Derive2*>(p);
	if (pd2 != nullptr) {
		pd2->new_func();
	}
	else {
		p->func();
	}
}

int main(){
	Derive1 d1;
	Derive2 d2;
	showFunc(&d1);
	showFunc(&d2);
	return 0;
}

在这里插入图片描述
使用static_cast,是编译时期的类型转换,不会判断运行时期的RTTI类型,由于这是上下继承关系,是一定可以转换成功的,这就导致我们无法判断Base指针指向的到底是哪个派生类对象

void showFunc(Base* p) {
	// 编译时期的类型转换
	Derive2* pd2 = static_cast<Derive2*>(p);
	if (pd2 != nullptr) {
		pd2->new_func();
	}
	else {
		p->func();
	}
}

int main(){
	Derive1 d1;
	Derive2 d2;
	showFunc(&d1);
	showFunc(&d2);
	return 0;
}

在这里插入图片描述
这种写法是肯定可以转换成功的,所以pd2不为空,无论Base指针导致指向的是哪个派生类对象,最后调用的肯定是Derive2::new_func。然而第一次调用showFunc传入的是Derive1对象的地址,还是调用了Derive2::new_func,这就很不安全

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值