C++四种强制类型转换

        有时希望显式地将对象强制转换成另一种类型。例如,如果想在下面的代码中执行浮点数除法:

int i,j;
double slope=i/j;

就要使用某种方法将i或j显式地转换成double,这种方法称作强制类型转换。

命名的强制类型转换

一个命名的强制类型转换有如下形式:

 cast-name<type>(expression);

        其中,type是转换的目标类型而expression是要转换的值。如果type是引用类型,则结果是左值。cast-name是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一种。dynamic_cast支持运行时类型识别。

static_cast转换

       任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。例如,通过将一个运算对象强制转换成double类型就能使表达式执行浮点数除法:

 //进行强制类型转换以便执行浮点数除法
 double slope=static_cast<double>(j)/i;

       当需要把一个较大的算术类型赋值给较小的类型时,static_cast非常有用。此时,强制类型转换告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失。一般来说,如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会给出警告信息;但是当我们执行了显式地类型转换后,警告信息就会被关闭了。

        static_cast对于编译器无法自动执行的类型转换也非常有用。例如,我们可以使用static_cast找回存在于void*指针中的值:

 void* p=&d;   //正确:任何非常量对象的地址都能存入void*
 
 double *dp=static_cast<double*>p;//正确:将void*转换回初始的指针类型

       当我们把指针存放在void*中,并使用static_cast将其强制转换回原来的类型时,应该确保指针的值保持不变。也就是说,强制类型转换的结果将与原始的地址的值相等,因此我们必须确保装换后所得的类型就是指针所指的类型。类型一旦不符,将产生未定义的后果。

dynamic_cast转换

dynamic_cast运算符的使用形式如下所示:

 dynamic_cast<type*>(e)
 dynamic_cast<type&>(e)
 dynamic_cast<type&&>(e)

       其中,type必须是一个类型,并且通常情况下该类型应该含有虚函数。在第一种形式中,e必须是一个有效的指针;在第二种形式中,e必须是一个左值;在第三种形式中,e不能是左值。
       在上面的所有形式中,e的类型必须符合以下三个条件中的任意一个:e的类型是目标type的公有派生类、e的类型是目标type的公有基类或者e的类型就是目标type的类型。如果符合,则类型转换可以成功。否则,转换失败。如果一条dynamic_cast语句的转换目标是指针类型并且失败了,则结果为0.如果转换目标是引用类型并且失败了,则dynamic_cast运算符将抛出一个bad_cast异常。

  • 指针类型的dynamic_cast

       举个简单的例子,假定Base类至少含有一个虚函数,Derived是Base的公有派生类。如果有一个指向Base的指针bp,则我们可以在运行时将它转换成Derived的指针,具体代码如下:

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

       如果bp指向Derived对象,则上述的类型转换初始化dp并令其指向bp所指的Derived对象。此时,if语句内部使用Derived操作的代码是安全的。否则,类型转换的结果为0,dp为0意味着if语句的条件失败,此时else子句执行相应的Base操作。
       我们可以对一个空指针执行dynamic_cast,结果是所需类型的空指针。值得注意的一点是,我们在条件部分定义了dp,这样做的好处是可以在一个操作中同时完成类型转换和条件检查两项任务。而且,指针dp在if语句外部都是不可访问的。一旦转换失败,即使后续的代码忘了做相应判断,也不会接触到这个未绑定指针,从而确保程序是安全的。

  • 引用类型的dynamic_cast

       引用类型的dynamic_cast与指针类型的dynamic_cast在表示错误发生的方式上略有不同。因为不存在所谓的空引用,所以对于引用类型来说无法使用与指针类型完全相同的错误报告策略。当对引用的类型转换失败时,程序抛出一个名为std::bad_cast的异常,该异常定义在typeinfo标准库头文件中。看下面的例子:

void f(const Base &b)
{
    try{
        const Derived &d=dynamic_cast<const Derived&>(b);
       //使用b引用的Derived对象
     }catch (bad_cast){
        //处理类型转换失败的情况
     }
}

const_cast转换

const_cast只能改变运算对象的底层const。 

 const char *pc;
 char *p=const_cast<char*>(pc);//正确:但是通过p写值是未定义的行为

        对于将常量对象转换成非常量对象的行为,一般称其为“去掉const性质”。一旦去掉了某个对象的const性质,编译器就不再阻止对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果。

        只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型:

const char *cp;
char *q=static_cast<char*>(cp);  //错误:static_cast不能转换掉const性质
static_cast<string>(cp);  //正确:字符创字面值转换成string类型
const_cast<string>(cp);   //错误:const_cast只改变常量属性
const_cast常常用于有函数重载的上下文中。

reinterpret_cast转换

reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。举个例子,假设有如下转换

int *ip;
char *pc=reinterpret_cast<char*>(ip);

我们必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。例如:

string str(pc);

可能导致异常的运行时行为。
       使用reinterpret_cast时非常危险的,用pc初始化str的例子很好地证明了这一点。其中的关键问题是类型改变了,但是编译器没有给出任何警告或者错误的信息。当我们使用一个int的地址初始化pc时,由于显式地声称这种转换合法,所以编译器不会发出任何警告或错误信息。接下来再使用pc时就会认定它的值是char*类型,编译器没法知道它实际存放的是指向int的指针。最终的结果是,在上面的例子中虽然用pc初始化str没什么实际意义,甚至还可能引发更糟糕的后果,但仅从语法上而言这种操作无可指摘。查找这类问题的原因非常困难,如果将ip强制转换成pc的语句和用pc初始化string对象的语句分属不同文件就更是如此。

旧式的强制类型转换

       在早期版本的C++语言中,显式地进行强制类型转换包含两种形式:   

 type (expr)   //函数形式的强制类型转换
 (type) expr   //C语言风格的强制类型转换

       根据所涉及的类型不同,旧式的强制类型转换分别具有与const_cast、static_cast或reinterpret_cast相似的行为。当我们在某处执行旧式的强制转换时,如果换成const_cast和static_cast也合法,则其行为对应相应的命名转换一致。如果替换后不合法,则旧式强制类型转换执行与reinterpret_cast类似的功能:

char *pc=(char*)ip;  //ip是指向整数的指针

的效果与使用reinterpret_cast一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值