C++提供六种类型转换符号。前两种是C风格,称为C风格强制转换,(T)或者T(),他们的效果一样。后面四种分别是:
负责将const类型转换为非const类型,也可以反过来转换。
通常非const类型会自动转换成const类型,所以一般不需要使用const_cast进行转换,但是如果有重载函数或者模板特化的话,还是有用的,请看下面的例子:
void f(string& str)
{
cout<<str<<endl;
}
void f(string const& str)
{
cout<<str<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
string str("f");
f(const_cast<string const&>(str));
return 0;
}
将调用f(string const& str)版本。
使用const_cast强制将原本const类型转换成非const类型是危险的行为。因为当你声明一个变量是const类型时,编译器有可能将该常量放在ROM中或者受到写保护的RAM页中。当你又试图转换这种使用物理手段来保护的常量时,经常会表现为内存故障。所以,尽可能的避免这样做,而且要意识到,一旦你让一个变量声明为常量,你就应该永不回头。
1)不能替代const_cast
C++标准规定static_cast不能用来将常量类型转换为非常量类型,那要依赖于const_cast。
2)等价于创建临时变量
static_cast可以用于达到类似于创建无具名临时变量的效果,比如:
void f(string const& str)
{
cout<<str<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
f(static_cast<string>("h"));
f(string("h"));//你可以理解为C风格强类型转换,也可以理解为创建无具名临时变量
return 0;
}
两个对于f函数的调用都正确,并且标准规定了static_cast<string>(“h”)和string(“h”)效果一样。
3)支持所有隐式转换
编译器支持的隐式转换操作都可以用static_cast完成。当较大的算术类型赋给较小的算术类型时,static_cast将关闭编译器数据有可能丢失的警告。
4)比较安全
static_cast 在编译时使用类型信息执行转换, 在转换执行必要的检测(诸如指针越界计算, 类型检查). 其操作相对是安全的.
int n=9;
double d=static_cast < double > (n);
上面的例子中, 将一个变量从 int 转换到 double. 这些类型的二进制表达式是不同的. 要将整数 9 转换到 双精度整数 9, static_cast 需要正确地为双精度整数 d 补足比特位. 其结果为 9.0
因为它比较安全,所以像下面我这样乱来就不能被编译器接受:
string str;
int* p=static_cast<int*>(&str);
error C2440: 'static_cast' : cannot convert from 'std::string *__w64 ' to 'int *'
相比而言,C风格的强转就是不安全的,因为下面的代码编译器允许:
string str;
int* p=(int*)&str;
说static_cast只是比较安全,是因为这样的代码居然还是能通过:
int x=9;
string* p=static_cast<string*>(&x);
5)将void*转换成T*
6)支持向下转型
static_cast可以将指向基类的指针强制转换成指向子类的指针。这不是dynamic_cast做的事情么?它们有什么区别呢?
简单来说,static_cast速度更快,安全性更差,dynamic_cast性能较差,但是更加严格和安全。
dynamic_cast将基类类型对象的引用或者指针转换为同一继承层次中的派生类型的引用或指针。
dynamic_cast涉及运行时类型检查,如果转换指针失败,将返回0,如果转换引用失败,将抛出bad_cast类型的异常。
dynamic_cast首先检查被请求的转换是否有效,如果有效才进行实际的转换。
dynamic_cast的性能是不高的,如果继承的层次较深的话,影响更大,但这不是你转而使用不太安全的static_cast的理由。
比较好的做法是避免向下转型,基类提供更多的虚函数或者直接使用子类都是候选的办法。
reinterpret_cast是强制编译器接收程序员主观认定的类型。它的行为不同于相对谨慎的static_cast, reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换,上面static_cast的例子修改一下:
int n=9;
double d=reinterpret_cast<double & > (n);
这次, 结果有所不同. 在进行计算以后, d 包含无用值. 这是因为 reinterpret_cast 仅仅是复制 n 的比特位到 d, 没有进行必要的分析.所以小心使用reinterpret_cast,和C风格的强转一样的不安全。
不仅如此,reinterpret_cast的执行后果根据不同的编译器上行为很可能不一样,没有可移植性。
既不安全又没有可移植性,那要它干么?因为有些与特定系统相关的底层编程,要求使用它在端口串行输入和输出数据,或者转换某些地址中的整数。
作为一个较安全的措施,我们可以这样干:
T1* p1=...;
T2* p2=reinterpret_cast<T2*>(p1)
第二行换成如下代码:
void* pV=p1;
T2* p2=static_cast<T2*>(pV);
尽量少用reinterpret_cast,如果实在不行,试试用static_cast替代来降低风险提高移植性。
当你写下任何一种转换操作,编译器都会在幕后写出一些代码,比如找到目标类型的类型信息和内存布局,以便执行转换。
假如你写下如下C风格代码:
class Derived;
void Gun(Base* pb)
{
Derived* pd=(Derived*)pb;
}
你没有#include “Derived.h”,而是通过前置声明了Derived类型,C风格转换会让你编译通过,但是由于看不到Derived.h文件,所以编译器会假定Base和Derived是两个不相关的类型,不会对对象内存布局作必要的调整,结果是运行时可能莫名其妙崩溃。
而这种情况下如果你使用了C++风格的转换,将会提示不能转换的错误,便于保证程序的正确性。
C++风格的转换操作符更容易搜索。