【C++】C++类型转换

C++类型转换

C语言的类型转换风格很简单,就是一个括号然后加上要转换的类型,例如(int)a。
而C++中提供了4种类型转换
1.const_cast<>();
2.static_cast<>();
3.dynamic_cast<>();
4.reinterpret_cast<>();

1.const_cast

const_cast的用法是:
用于修改类型的const和volatile属性
常量指针被转化成非常量指针,并且仍然指向原来的对象。
常量引用被转换成非常量引用,并且仍然指向原来的对象;
常量对象被转换成非常量对象。

const int a=10;
int b=const_cast<int>(a);//conpile error

这是一段错误的代码,因为const_cast强制转换对象必须为指针或者引用
现在我们知道,const_cast可以去除一个指针或者引用的const属性,那么来看下面这段代码

const int a=10;
int* b=const_cast<int*>(&a);
*b=20;
cout<<&a<<endl;
cout<<b<<endl;
cout<<a<<endl;
cout<<*b<<endl;

结果:
在这里插入图片描述
在这段代码中我们发现const常量a的地址和去const之后的指针b指向的地址是一样的,但是我们修改了b的值之后,相同地址的a的值并没有改变。
?????
是不是觉得很疑惑,为什么会发生这种事情?
虽然很疑惑,但是我们应该庆幸我们的常量a并没有被改变。如果常量a被改变了,在这样的小程序里面我们可以知道常量a被改变了并不是什么大事情。
但是如果是在一个很复杂的工程里面,我们用const_cast去除const限定之后,特别是当这个const常量是你团队中的其他成员写的时候,那这个时候你们的整个工程都乱套了。
所以我们要庆幸const常量a并没有被改变。
而a没有被改变的原因是

IBM的C++指南称呼“*modifier = 7;”为“未定义行为(UndefinedBehavior)”。所谓未定义,是说这个语句在标准C++中没有明确的规定,由编译器来决定如何处理。

位运算的左移操作也可算一种未定义行为,因为我们不确定是逻辑左移,还是算数左移。

再比如下边的语句:v[i] = i++; 也是一种未定义行为,因为我们不知道是先做自增,还是先用来找数组中的位置。

对于未定义行为,我们所能做的所要做的就是避免出现这样的语句。对于const数据我们更要这样保证:绝对不对const数据进行重新赋值。

那既然我们不想改变const的值,那这个const_cast函数看起来似乎没什么用了,因为他并不能真的改变一个const常量的值。
但是其实我们的去const限定还有一个这样的作用

#include<iostream>

using namespace std;

void Test(int* a)
{
	cout<<*a;
} 

int main()
{
	const int num=10;
	Test(const_cast<int*>(&num));
} 

如以上的代码,当我们在一个项目中的时候,如果我们需要调用一个别人已经写好的函数,这个函数要求一个指针做为参数,而我们知道这个函数并不会改变这个指针的值的时候,我们有一个const常量需要传入这个函数来做一些运算。
要么我们再定义一个变量复制这个const常量的值,但是如果我们要传入的指针对象是一个很复杂的类的实例的时候,我们并不希望去浪费时间和空间去复制这个对象。
那么这个时候,const_cast的能力就体现出来了。
使用const_cast,我们可以去除这个常量的const限定,把他的值传入这个函数中进行运算。
具体更多的有关const_cast函数的分析,可以看这篇文章,个人觉得写的非常不错。
https://blog.csdn.net/TanJiaLiang_/article/details/83992337

2.static_cast

C++语言中static_cast用于内置数据类型的强制转换。

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

static_cast主要有如下几种用法:
1.用于类层次结构中基类和派生类之间的指针或者引用的转化
上行转换(子类转换成父类)是安全的
下行转换(父类转换成子类)是不安全的,因为没有动态类型检查
2.基本类型的转换,如int和flaot之间的转换,int和char之间的转换,需要开发人员保证安全。
3.把空指针转换成目标类型的空指针
4.把任何类型的表达式转换为void类型

如果涉及到类的话,static_cast只能在有相互联系的类型中进行相互转换,不一定包含虚函数。

要注意的是,当我们使用static_cast向下转换,也就是又父类指针转换到子类指针的时候,代码编译运行是可以通过的,但是不推荐使用static_cast来进行这样的转换。

#include<iostream>

using namespace std;

class A
{
public:
	A()
	{
		cout << "A constructor"<<endl;
	}

	~A()
	{
		cout << "A destroyed" << endl;
	}
	virtual void Vfunc()
	{
		cout << "AVfunc" << endl;
	}

	virtual void NVfunc()
	{
		cout << "ANVfunc" << endl;
	}
	void Nfunc()
	{
		cout << "ANfunc" << endl;
	}


protected:
	int AProtectedNum;
private:
	int APrivateNum;
};


class B :public A
{
public:
	B()
	{
		cout << "B constructor" << endl;
	}

	~B()
	{
		cout << "B destroyed" << endl;
	}

	virtual void Vfunc()
	{
		cout << "BCfunc" << endl;
	}
	void NVfunc()
	{
		cout << "NVfunc" << endl;
	}
	void Nfunc()
	{
		cout << "Nfunc" << endl;
	}
};


int main()
{
	A* a1 = new A();
	A* a2 = new B();
	B* ab1=static_cast<B*>(a1);
	B* ab2 = static_cast<B*>(a2);
	cout << endl;
	a1->Vfunc();
	a1->NVfunc();
	a1->Nfunc();
	cout << endl;
	a2->Vfunc();
	a2->NVfunc();
	a2->Nfunc();
	cout << endl;
	ab1->Vfunc();
	ab1->NVfunc();
	ab1->Nfunc();
	cout << endl;
	ab2->Vfunc();
	ab2->NVfunc();
	ab2->Nfunc();
	cout << a1<<endl;
	cout << a2 << endl;
	cout << ab1 << endl;
	cout << ab2 << endl;
} 

运行结果
在这里插入图片描述
从中这个运行结果中可以看出,static_cast确实是可以让父类转为子类的。
但是在这个运行结果中,ab1想要把a1指针转换为一个子类B的指针,通过static_cast的方式,但是a1指针指向的是一个类A,所以显然是无法转换的,但是static_cast没有报错,而是将这个类A的指针保留了下来传递给了ab1,这就是不安全的情况。

在我们的预期中,ab1这个指针应该转换为了类B的指针,执行类B的方法,但是他转换失败了,而系统没有任何的提示,这在我们的系统中是不安全的。
所以在向下转换,也就是父类转向子类的时候,切记不要使用static_cast,请使用dynamic_cast

3.dynamic_cast

dynamic_cast与其他3种类型转换不同的是,其他3种类型转换都是在编译期完成的,而dynamic_cast是在运行时处理, 在运行是要进行类型检查。
dynamic_cast主要用于有虚函数的类的类型转换。
关于dynamic_cast8有一下几个要点
1.dynamic_cast是在运行是处理,在运行时进行类型检查的。
2.dynamic_cast不能用于内置的基本数据类型的强制转换
3.dynamic_cast转换如果成功的话返回的是指向类的指针或者引用,转换失败的话会返回NULL
4.使用dynamic_cast进行转换是,基类中一定要有虚函数,否则编译不通过。
5.在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
6. 向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

4.reinterpret_cast

在C++语言中,reinterpret_cast主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。

用法:reinterpret_cast<type_id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。
在使用reinterpret_cast强制转换过程仅仅只是比特位的拷贝,因此在使用过程中需要特别谨慎!

例7:

int *a = new int; double *d = reinterpret_cast<double *>(a);

在例7中,将整型指针通过reinterpret_cast强制转换成了双精度浮点型指针。
reinterpret_cast可以将指针或引用转换为一个足够长度的整形,此中的足够长度具体长度需要多少则取决于操作系统,如果是32位的操作系统,就需要4个字节及以上的整型,如果是64位的操作系统则需要8个字节及以上的整型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值