c 语言强制类型转换主要用于基础的数据类型间的转换,语法为:
(type-id) expression; //转换格式1
type-id (expression); //转换格式2
c++ 除了能使用 c 语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast,主要运用于继承关系类间的强制转化,语法为:
static_cast (expression)
dynamic_cast (expression)
const_cast (expression)
reinterpret_cast (expression)
备注:new_type 为目标数据类型,expression 为原始数据类型变量或者表达式。
《Effective C++》中将 c 语言强制类型转换称为旧式转型,c++ 强制类型转换称为新式转型。
1:const_cast
const_cast 用于修改类型的 const 或 volatile 属性。 该运算符用来修改类型的 const (唯一有此能力的 C++-style 转型操作符) 或 volatile 属性。除了 const 或 volatile 修饰之外, new_type 和 expression 的类型是一样的。
①常量指针被转化成非常量的指针,并且仍然指向原来的对象;
②常量引用被转换成非常量的引用,并且仍然指向原来的对象;
③const_cast 一般用于修改底指针。如 const char *p 形式。
举例转换如下:
const int g = 20;
int *h = const_cast(&g); //去掉const常量const属性
const int g = 20;
int &h = const_cast(g); //去掉const引用const属性
const char *g = "hello";
char *h = const_cast(g); //去掉const指针const属性
2:dynamic_cast
dynamic_cast(e)
dynamic_cast(e)
dynamic_cast(e)
type 必须是一个类类型,在第一种形式中,type 必须是一个有效的指针,在第二种形式中,type 必须是一个左值,在第三种形式中,type 必须是一个右值。在上面所有形式中,e 的类型必须符合以下三个条件中的任何一个:e 的类型是是目标类型 type 的公有派生类、e 的类型是目标 type 的共有基类或者 e 的类型就是目标 type 的的类型。如果一条 dynamic_cast 语句的转换目标是指针类型并且失败了,则结果为 0。如果转换目标是引用类型并且失败了,则 dynamic_cast 运算符将抛出一个 std::bad_cast 异常 (该异常定义在 typeinfo 标准库头文件中)。e 也可以是一个空指针,结果是所需类型的空指针。
dynamic_cast 主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换(cross cast)。
在类层次间进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的;
在进行下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。dynamic_cast 是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作。
(1)指针类型
举例,Base 为包含至少一个虚函数的基类,Derived 是 Base 的共有派生类,如果有一个指向 Base 的指针 bp,我们可以在运行时将它转换成指向 Derived 的指针,代码如下:
if(Derived *dp = dynamic_cast(bp)){
//使用dp指向的Derived对象
}
else{
//使用bp指向的Base对象
}
值得注意的是,在上述代码中,if 语句中定义了 dp,这样做的好处是可以在一个操作中同时完成类型转换和条件检查两项任务。
(2)引用类型
因为不存在所谓空引用,所以引用类型的 dynamic_cast 转换与指针类型不同,在引用转换失败时,会抛出 std::bad_cast 异常,该异常定义在头文件 typeinfo 中。
void f(const Base &b){
try{
const Derived &d = dynamic_cast(b);
//使用b引用的Derived对象
}
catch(std::bad_cast){
//处理类型转换失败的情况
}
}
3:static_cast
static_cast 相当于传统的 C 语言里的强制转换,该运算符把 expression 转换为 new_type 类型,用来强迫隐式转换,例如 non-const 对象转为 const 对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
②用于基本数据类型之间的转换,如把 int 转换成 char,把 int 转换成 enum。这种转换的安全性也要开发人员来保证。
③把空指针转换成目标类型的空指针。
④把任何类型的表达式转换成 void 类型。
注意:static_cast 不能转换掉 expression 的 const、volatile、或者__unaligned 属性。
基本类型数据转换举例如下:
char a = 'a';
int b = static_cast(a);//正确,将char型数据转换成int型数据
double *c = new double;
void *d = static_cast(c);//正确,将double指针转换成void指针
int e = 10;
const int f = static_cast(e);//正确,将int型数据转换成const int型数据
const int g = 20;
int *h = static_cast(&g);//编译错误,static_cast不能转换掉g的const属性
类上行、下行的转换:
if(Derived *dp = static_cast(bp)){//下行转换是不安全的
//使用dp指向的Derived对象
}
else{
//使用bp指向的Base对象
}
if(Base*bp = static_cast(dp)){//上行转换是安全的
//使用bp指向的Derived对象
}
else{
//使用dp指向的Base对象
}
4: reinterpret_cast
new_type 必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。
reinterpret_cast 意图执行低级转型,实际动作(及结果)可能取决于编辑器,这也就表示它不可移植。
举一个错误使用 reintepret_cast 例子,将整数类型转换成函数指针后,vc++ 在执行过程中会报 "... 中的 0xxxxxxxxx 处有未经处理的异常: 0xC0000005: Access violation" 错误:
#include
using namespace std;
int output(int p){
cout << p <(&p);
fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation
return 0;
}
IBM 的 C++ 指南、C++ 之父 Bjarne Stroustrup 的 FAQ 网页和 MSDN 的 Visual C++ 也都指出:错误的使用 reinterpret_cast 很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用 reinterpret_cast 方式。
MSDN 中也提到了,实际中可将 reinterpret_cast 应用到哈希函数中,如下(64 位系统中需将 unsigned int 修改为 unsigned long):
// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include
// Returns a hash code based on an address
unsigned short Hash( void *p ) {
unsigned int val = reinterpret_cast( p );
return ( unsigned short )( val ^ (val >> 16));
}
using namespace std;
int main() {
int a[20];
for ( int i = 0; i < 20; i++ )
cout << Hash( a + i ) << endl;
}
另外,static_cast 和 reinterpret_cast 的区别主要在于多重继承,比如
class A {
public:
int m_a;
};
class B {
public:
int m_b;
};
class C : public A, public B {};
例子:
C c;
printf("%p, %p, %p", &c, reinterpret_cast(&c), static_cast (&c));
前两个的输出值是相同的,最后一个则会在原基础上偏移 4 个字节,这是因为 static_cast 计算了父子类指针转换的偏移量,并将之转换到正确的地址(c 里面有 m_a,m_b,转换为 B * 指针后指到 m_b 处),而 reinterpret_cast 却不会做这一层转换。
因此,你需要谨慎使用 reinterpret_cast。
注意事项: