C++中的4种显式类型转换

命名的强制类型转换符号的一般形式如下:

cast-name<type>(expression);

其中,cast-name为const_cast,static_cast,dynamic_cast和reinterpret_cast之一,type为转换的目标类型,而expression则是被强制转换的表达式。

const_cast

const_cast只能改变运算对象的底层的const。底层const表示指针或引用所指的对象是一个常量,顶层const表示指针本身是个常量。对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉const性质”。一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。

比如:

#include <iostream>
using namespace std;
int main()
{
	int i = 2;
	const int *p=&i;//p是常量指针(指向常量的指针)
	int *q = const_cast<int *>(p);//去掉const性质
	*q = 5;	
	cout << *p << "  " << *q << endl;
	system("pause");
	return 0;
}

static_cast

用法:static_cast < type-id > ( expression ),

该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性,它主要有如下几种用法:

(1)用于基本数据类型之间的转换,如把int转换为char,把int转换成enum,但这种转换的安全性需要开发者自己保证(这可以理解为保证数据的精度,即程序员能不能保证自己想要的程序安全),如在把int转换为char时,如果char没有足够的比特位来存放int的值(int>127或int<-127时),那么static_cast所做的只是简单的截断,及简单地把int的低8位复制到char的8位中,并直接抛弃高位。

(2)把空指针转换成目标类型的空指针

(3)把任何类型的表达式类型转换成void类型

(4)用于类层次结构中父类和子类之间指针和引用的转换。

对于以上第(4)点,存在两种形式的转换,即上行转换(子类到父类)和下行转换(父类到子类)。对于static_cast,上行转换时安全的,而下行转换时不安全的,为什么呢?因为static_cast的转换时粗暴的,它仅根据类型转换语句中提供的信息(尖括号中的类型)来进行转换,这种转换方式对于上行转换,由于子类总是包含父类的所有数据成员和函数成员,因此从子类转换到父类的指针对象可以没有任何顾虑的访问其(指父类)的成员。而对于下行转换为什么不安全,是因为static_cast只是在编译时进行类型坚持,没有运行时的类型检查,具体原理在dynamic_cast中说明。

一句话概括:

仅当类型之间可隐式转换时(除类层次间的下行转换以外),static_cast的转换才是合法的,否则将产生错误。

dynamic_cast

用法:该运算符把expression转换为type类型的对象。type必须是类的指针、类的引用或者void*。如果type是指针类型,那么expression也必须是一个指针,如果type是一个引用,那么expression也必须是一个引用。

注意static_cast只在编译时进行类型检查,而dynamic_cast是运行时类型检查,则需要视情况而定

与其他强制类型转换不同,dynamic_cast涉及运行时时类型检测。dynamic_cast运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才会有虚函数表,没有定义虚函数的类是没有虚函数表的,对没有虚函数表的类使用会导致dynamic_cast编译错误。

所以dynamic_cast主要用于类层次结构中父类和子类之间指针和引用的转换,由于具有运行时类型检查,因此可以保证下行转换的安全性,何为安全性?即转换成功就返回转换后的正确类型指针,如果转换失败,则返回NULL(如果是转换到月引用类型的dynamic_cast失败,则抛出bad_cast类型的异常),之所以说static_cast在下行转换时不安全,是因为即使转换失败,它也不返回NULL。

对于上行转换,dynamic_cast和static_cast是一样的。

但是在下行转换中,存在两种情况:

class Base
{
	virtual void fun() {}
};
class Derived :public Base
{
};

由于需要进行向下转换,因此需要定义一个父类类型的指针Base *p但是由于子类继承于父类,父类指针可以指向父类对象,也可以指向子类对象,这就是重点所在。如果P指向的确实是子类对象,则dynamic_cast和static_cast都可以转换成功,如下所示:

Base *P = new Derived;
Derived *pd1 = static_cast<Derived *>(P);
Derived *pd2 = dynamic_cast<Derived *>(P);

以上转换都能成功。

但是,如果 P 指向的不是子类对象,而是父类对象,如下所示

Base *P = new Base;
Derived *pd3 = static_cast<Derived *>(P);
Derived *pd4 = dynamic_cast<Derived *>(P);

在以上转换中,static_cast转换在编译时不会报错,也可以返回一个子类对象指针(假想),但是这样是不安全的,在运行时可能会有问题,因为子类中包含父类中没有的数据和函数成员,这里需要理解转换的字面意思,转换是什么?转换就是把对象从一种类型转换到另一种类型,如果这时用 pd3 去访问子类中有但父类中没有的成员,就会出现访问越界的错误,导致程序崩溃。而dynamic_cast由于具有运行时类型检查功能,它能检查P的类型,由于上述转换是不合理的,所以它返回NULL。

reinterpret_cast

允许将任何指针转换为任何其他指针类型。 也允许将任何整数类型转换为任何指针类型以及反向转换。看着上面的描述就有种放浪形骸的赶脚。更会让人不寒而栗,太随意!

int *p;
char *q = reinterpret_cast<char *>(p);
int i = 3;
char *c = nullptr;
c = reinterpret_cast<char *>(i);

注意:

滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值