强制类型转换

static_cast

任何具有明确定义的类型转换,只要不包含底层 const,都可以使用 static_cast。

例如,通过将一个运算对象强制转换成 double 类型就能使表达式执行浮点数除法∶

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

当需要把一个较大的算术类型赋值给较小的类型时,static_cast 非常有用。

此时,强制类型转换告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失。

一般来说,如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会给出警告信息;但是当我们执行了显式的类型转换后,警告信息就会被关闭了。

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

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

const_cast

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

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

对于将常量对象转换成非常量对象的行为,我们一般称其为"去掉 const性质(cast away the 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 对象的语句分属不同文件就更是如此。

建议:避免强制类型转换。

强制类型转换干扰了正常的类型检查,因此我们强烈建议程序员避免使用强制类型转换。这个建议对于reinterpret_cast尤其适用,因为此类类型转换总是充满了风险。

在有重载函数的上下文中使用 const_cast无可厚非,但是在其他情况下使用 const_cast也就意味着程序存在某种设计缺陷。

其他强制类型转换,比如 static_cast 和 dynamic_cast,都不应该频繁使用。每次书写了一条强制类型转换语句,都应该反复斟酌能否以其他方式实现相同的目标。

就算实在无法避免,也应该尽量限制类型转换值的作用域,并且记录对相关类型的所有假定,这样可以减少错误发生的机会。

旧式的强制类型转换

在早期的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一样。

posted @ 2021-01-20 16:18  DearLeslie  阅读(213)  评论(0编辑  收藏  举报