谈谈C++的四种“cast”类型转换

本文介绍了C++中4种cast类型转换。static_cast可用于大多明确定义的类型转换,但不能处理底层const;const_cast能去掉底层const属性,在重载函数中有用;reinterpret_cast为位模式提供低层次重新解释;dynamic_cast用于运行时在父子类间转换。使用类型转换需谨慎。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相比于传统的类型转换,C++中的4种cast作用更加明确,使代码更具有可读性,下面就来依次介绍一下:

1.static_cast

任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast,这几乎就是传统类型转换的替代(为什么是几乎,下文会提到),比如:

double a=10;
int b = static_cast<int>(a);

相比于传统的int b=(int)a,使用static_cast更像是告诉编译器“我知道转换会发生就精度的损失,但是我还是要进行转换”,所以使用static_cast的情况下,编译器也不会发出警告。

但如果想使用他来进行底层const的转换,就会产生问题,比如:

相比之下,传统的类型转换可以进行这样的转换,即使转换后在使用上会有些问题

const int a = 10;
const int *pa = &a;
int *p = (int*)pa;
*p = 11;
cout << *p << endl;
cout << a << endl;

这段代码在visual studio2017下诡异的输出11和10.

另外,如同下面这种类型的转换,用static_cast也是做不到的。

	const double a = 10;
	const double* pa = &a;
	char *p = (char*)(pa);

2.const_cast

const_cast转换可以去掉底层const属性,一旦我们去掉了底层const属性,编译器就不再阻止我们对const对象进行改变了;但尽管如此,我们还需要注意的是,如果我们试图将一个指向常量的指针转换为一个普通指针,再通过这个普通指针去修改原来的常量时,会产生未定义的行为:

#include<iostream>
using namespace std;

int main() {
	int a = 10;
	const int ac = 10;
	const int *pa = &ac;
	int *p = const_cast<int*>(pa);
	*p = 11;
	cout << *p << endl;
	cout << *pa << endl;
	cout << ac << endl;

	cout << p << endl;
	cout << pa << endl;
	cout << &ac << endl;
	return 0;
}

测试代码和运行结果如上;

就如上面的代码,我们试图将ac的值改为11,但在visual studio2017的编译环境下(在gcc 7.3.0下也是如此),我们发现,当我们直接访问ac时,ac的值仍然是10,但惊悚的是,当我们通过指针p和指针pa访问ac时,会发现ac的值变成了11,更惊悚的是,指针p、pa指向的地址和ac的地址一样!

另外,const_cast在重载函数中比较有用;

以下例子取自《C++primer 5th》209页:

const string& shorterString(const string &s1, const string &s2)
{
	return s1.size() <= s2.size() ? s1 : s2;
}
string& shorterString(string& s1, string& s2)
{
	auto &s = shorterString(const_cast<string&>(s1), const_cast<string&>(s2));
	return const_cast<string&>(s);
}

第一个版本的shorterString函数可以用来返回两个字符串常量当中长度小的那一个,但当我们需要非常量版本的时候,我们就可以使用const_cast轻易做到这一点,对于上面的那个例子,你可能会说,shorterString函数的逻辑如此简单,我再实现一遍也可以,但是,当我们函数的内部逻辑较为复杂的时候,这就是一种简洁明了的实现重载函数的方式。

3.reinterpret_cast

reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。(《C++primer 5th》145页)

通俗点说,就是你可以让一个char*把他所指向的对象当做char,即使他实际上并不是char,还是原来的类型;

double a = 10.5;
double* pa = &a;
int *p = reinterpret_cast<int*>(pa);

同样的,reinterpret_cast不能用来丢掉const,,要丢掉底层const,请使用const_cast。

另外,它也无法提供高精度向低精度的转换:

要使高精度转换到低精度,可以用传统的类型转换和static_cast。

4.dynamic_cast

C++的运行时类型识别功能由两个运算符实现:dynam_cast和typeid

其中,dynamic_cast的使用形式如下:

dynamic_cast<type*>(e)

dynam_cast<type&>(e)

dynamic_cast<type&&>(e)

这里的type必须为类类型,而且通常情况下有虚函数(毕竟主要用来实现多态功能)

如上所示,在第一种形式中,e必须为类的指针;在第二种形式中,e必须为类的左值引用;在第三种形式中,e必须为类的右值引用;

dynamic_cast用于运行时在父类和子类中向上或者向下转换,如果转换成功,则返回转换后的类型;在转换失败时,如果目标类型是指针,则返回0,目标类型为引用,则返回一个bad_cast异常。

举个使用的例子,假定Derived类是Base类的公有派生类,bp是一个Base类型的指针,而Base类至少有一个虚函数:

if(Derived* dp=dynamic_cast<Derived*>bp)//bp指向一个Derived对象
{
    //doSomthing...
}
else//bp指向一个Base对象
{
    //doSomthing...
}

使用应用的dynamic_cast可以参照使用指针的dynamic_cast,只是在转换失败时的表达略有不同。

最后说一句,使用任何类型转换都需要谨慎谨慎再谨慎,一定要保证转换前和转换后的结果都在自己的掌握之中,因为如果在类型转换上出了问题,最后都是比较难找的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值