一、C方式的类型转换
(1)语法:
(Type)( Expression )
Example:
#include <stdio.h>
typedef void(PF)(int);
struct Point
{
int x;
int y;
};
int main()
{
int v = 0x12345;
PF* pf = (PF*)v; //将int 类型的v 强制转换成函数指针PF
char c = char(v); //将int类型转换成char类型
Point* p = (Point*)v; //将int强制转换成结构体类型的指针
pf(5);
printf("p->x = %d\n", p->x);
printf("p->y = %d\n", p->y);
getchar();
return 0;
}
执行程序:
这是因为C语言在强制类型转换中,过于暴力,会发生地址截断。
(2)C方式强制类型转换存在的问题:
1.过于粗暴:任意类型之间都可以进行转换,编译器很难判断其正确性。 |
2.难于定位:在源码中无法快速定位所有使用强制类型转换的语句。 |
二、C++将强制类型转换为4中不同类型
static_cast | const_cast |
dynamic_cast | reinterpret_cast |
语法:
xxx_cast<Type>(Expression)
(1)static_cast强制类型转换:
static_cast可以在指向相关类的指针之间执行转换,不只是upcast(从pointer-to-derive 到 pointer-to-base),还可以进行向下转换(从pointer-to-base到pointer-to-derive )。在运行时不执行检查,以保证被转换的对象实际上是目的地类型的完整对象。因此,由程序员来确保转换是安全的。另一方面,它不会产生动态转换的类型安全检查的开销。
特点:
1.用于基本类型间的转换 |
2.不能用于基本类型指针之间的转换 |
3.用于有继承关系类对象之间的转换和类指针之间的转换 |
example:
class Base {};
class Derived: public Base {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
这是有效的代码,尽管b会指向类的不完整对象,如果取消引用,可能会导致运行时错误。
(2)const_cast强制类型转换:
这种类型的转换操纵指针所指向的对象的常量,要么被设置要么被移除。 例如,为了将const指针传递给期望非const参数的函数:
// const_cast
#include <iostream>
using namespace std;
void print (char * str)
{
cout << str << '\n';
}
int main () {
const char * c = "sample text";
print ( const_cast<char *> (c) );
return 0;
}
特点:
1.用于去除变量的只读属性 | |
2.强制转换的目标类型必须是指针或引用 |
(3)reinterpret_cast强制类型转换
reinterpret_cast可以将任何指针类型转换为任何其他指针类型,甚至是不相关的类。 操作结果是从一个指针到另一个指针的值的简单二进制副本。 允许所有指针转换:既不检查指向的内容,也不检查指针类型本身。
它还可以从整数类型转换指针。这个整型值表示指针的格式是特定于平台的。唯一的保证是,一个指针被投射到一个足够大的整数类型,保证能够将其转换回一个有效的指针。
特点:
1.用于指针类型间的强制转换 | |
2.用于整数和指针类型间的强制转换 |
Example:
class A { /* ... */ };
class B { /* ... */ };
A * a = new A;
B * b = reinterpret_cast<B*>(a);
分析:这段可以代码编译,尽管它没有多大意义,因为现在b指向一个完全不相关的、可能不兼容的类的对象a。
(4)dynamic_cast强制类型转换:
dynamiccast只能用于指针和对类(或void*)的引用。它的目的是为了确保类型转换的结果指向目标指针类型的有效完整对象。
特点:
1.用于有继承关系的类指针间的转换 |
2.用于有交叉关系的类指针间的转换 |
3.具有类型检查的功能 |
4.需要虚函数的支持 |
dynamic_cast测试用例:
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
class Base { virtual void dummy() {} };
class Derived : public Base { int a; };
int main() {
try {
Base * pba = new Derived; //pba指向对象的类型为Derived
Base * pbb = new Base; //pbb指向对象的类型为Base
Derived * pd;
//返回一个空指针来指示转换失败
pd = dynamic_cast<Derived*>(pba);
if (pd == 0) cout << "第一种类型转换得到空指针.\n";
pd = dynamic_cast<Derived*>(pbb);
if (pd == 0) cout << "第二种类型转换得到空指针.\n";
}
catch (exception& e) { cout << "Exception: " << e.what(); }
getchar();
return 0;
}
输出结果:
分析:
上面的代码尝试从类型Base*
(pba
和pbb
)的指针对象到类型的指针对象Derived*,执行两个动态强制转换,但只有第一个成功。 注意它们各自的初始化:
Base * pba = new Derived;
Base * pbb = new Base;
尽管两者都是基类的指针,但pba实际上指向的是派生类型的对象,而pbb指向类型基的对象。因此,当使用dynamic_cast来执行各自的类型转换时,pba指向一个派生类的完整对象,而pbb指向类基的对象,后者是派生类的不完整对象。
当dynamic_cast不能抛出一个指针时,因为它不是所要求的类的完整对象——就像例子中的第二个转换一样——它返回一个空指针来指示转换失败。如果dynamic_cast被用来转换为引用类型,并且转换是不可能成功的,那么就会抛出类型bad_cast的异常。
(5)总体分析实例
Example:
#include <stdio.h>
void static_cast_demo()
{
int i = 0x12345;
char c = 'c';
int* pi = &i;
char* pc = &c;
c = static_cast<char>(i);
//pc = static_cast<char*>(pi);//Error:无法从“int *”转换为“char *”,不能用于基本类型指针之间的转换
}
void const_cast_demo()
{
const int& j = 1;
int& k = const_cast<int&>(j);
const int x = 2;
int& y = const_cast<int&>(x);
//int z = const_cast<int>(x);//Error:无法从“const int”转换为“int”,强制转换的目标类型必须是指针或引用
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
y = 8;
printf("x = %d\n", x);
printf("y = %d\n", y);
printf("&x = %p\n", &x);
printf("&y = %p\n", &y);
}
void reinterpret_cast_demo()
{
int i = 0;
char c = 'c';
int* pi = &i;
char* pc = &c;
pc = reinterpret_cast<char*>(pi);
pi = reinterpret_cast<int*>(pc);
pi = reinterpret_cast<int*>(i);
//c = reinterpret_cast<char>(i);//error:无法从“int”转换为“char”,用于指针类型间的强制转换
}
void dynamic_cast_demo()
{
int i = 0;
int* pi = &i;
//char* pc = dynamic_cast<char*>(pi); //error: dynamic_cast 的目标类型无效,需要虚函数的支持
}
int main()
{
static_cast_demo();
const_cast_demo();
reinterpret_cast_demo();
dynamic_cast_demo();
getchar();
return 0;
}
编译运行,输出结果:
<本文完>
参考资料: