008 显示转换 static_cast dynamic_cas
static_cast
任何具有明确定义的类型转换,只要不包含底层 const,都可以使用 static_cast 例如,通过将一个运算对象强制转换成 doub1e 类型就能使表达式执行浮点数除法:
int j = 20, i = 3;
double r = static_cast<double>(j) / i;
qDebug() << r; // 6.66667
当需要把一个较大的算术类型赋值给较小的类型时,static_cast 非常有用。此时强制类型转换告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失。一般来说如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会给出警告信息;但是当我们执行了显式的类型转换后,警告信息就会被关闭了。
static_cast 对于编译器无法自动执行的类型转换也非常有用。例如,我们可以使用 static_cast 找回存在于 void* 指针中的值:
int j = 20;
void* p = &j;
int *pv = static_cast<int*>(p);
qDebug() << *pv; // 20
当我们把指针存放在 void* 中,并且使用 static_cast 将其强制转换回原来的类型时应该确保指针的值保持不变。也就是说,强制转换的结果将与原始的地址值相等,因此我们必须确保转换后所得的类型就是指针所指的类型。类型一旦不符,将产生未定义的后果。
const_cast
const_cast 只能改变运算对象的底层 const:
#include <QCoreApplication>
#include <cxxabi.h>
#include <QDebug>
#include <typeinfo>
#include <iostream>
#include <string>
#include <memory>
#include <cstdlib>
#include <vector>
namespace {
std::string demangle(const char* mangled)
{
int status;
std::unique_ptr<char[], void (*)(void*)> result(
abi::__cxa_demangle(mangled, nullptr, nullptr, &status), std::free);
return result.get() ? std::string(result.get()) : "error occurred";
}
template<class T>
void foo(T t) { std::cout << demangle(typeid(t).name()) << std::endl; }
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const char *pc = "test";
char *p = const_cast<char*>(pc);
foo(pc); // char const*
foo(p); // char*
return a.exec();
}
对于将常量对象转换成非常量对象的行为,我们一般称其为“去掉 const性质( cast awaythe const)”。一旦我们去掉了某个对象的 const 性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用 const_cast 执行写操作就会产生未定义的后果。
只有 const_cast 能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用 const_cast 改变表达式的类型:
const char *pc = "test";
char *p = const_cast<char*>(pc);
char *q = static_cast<char*>(pc); // 错误
std::string str = static_cast<std::string>(pc); // 正确
std::string str1 = const_cast<std::string>(pc); // 错误
const_cast 常常用于有函数重载的上下文中。
reinterpret_cast
reinterpret_cast 通常为运算对象的位模式提供较低层次上的重新解释。举个例子,假设有如下的转换:
int *ip;
char *pc = reinterpret_cast<char*>(ip);
我们必须牢记 pc 所指的真实对象是一个 int 而非字符,如果把 pc 当成普通的字符指针使用就可能在运行时发生错误。例如:
int test = 10;
int *ip = &test;
char *pc = reinterpret_cast<char*>(ip);
std::string str(pc);
qDebug() << QString::fromStdString(str); // 输出 "\n"
使用 reinterpret_cast 是非常危险的,用 pc 初始化 str 的例子很好地证明了这点。其中的关键问题是类型改变了,但编译器没有给出任何警告或者错误的提示信息当我们用一个 int 的地址初始化 pc 时,由于显式地声称这种转换合法,所以编译器不会发出任何警告或错误信息。接下来再使用 pc 时就会认定它的值是 char* 类型,编译器没法知道它实际存放的是指向 int 的指针。最终的结果就是,在上面的例子中虽然用 pc 初始化 str 没什么实际意义,甚至还可能引发更糟糕的后果,但仅从语法上而言这种操作无可指摘。查找这类问题的原因非常困难,如果将 ip 强制转换成 pc 的语句和用 pc 初始化 string 对象的语句分属不同文件就更是如此。