static_cast、dynamic_cast、reinterpret_cast和const_cast之间的区别
static_cast
用法:static_cast<type-id>(exdivssion)
,该运算符把exdivssion转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
用于类层次结构中父类和子类之间指针或引用的转换。
- 进行上行转换(把子类的指针或引用转换成父类表示),这是安全的。
- 进行下行转换(把父类的指针或引用转换成子类表示),由于没有动态类型检查,所以是不安全的
用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
- 把空指针转换成目标类型的空指针。
- 把任何类型的表达式转换成void类型。
PS:static_cast 不能转换掉exdivssion的
const
、volitale
、或者__unaligned
属性。
int main(void)
{
//基本类型间的转换,需要用户保证安全
int a = 1000;
char c = static_cast<char>(a);//不安全,1000超过了char的表示范围
cout<<c<<endl;//输出空
a = 49;
c = static_cast<char>(a);//安全,输出字符‘1’
cout<<c<<endl;
//c = dynamic_cast<char>(a); 错误
cout<<"--------------------------------------------\n";
//void *和基本类型指针的转换,需要用户保证转换安全
a = 49;
void *pv;
pv = &a;
int *pi = static_cast<int *>(pv);//void * 转换为int *
cout<<*pi<<endl; //输出49
//pi = dynamic_cast<int *>(pv); 错误
char *pc = static_cast<char *>(pv);//void *转char*
cout<<*pc<<endl;//输出字符‘1’
void *pv2 = static_cast<void *>(pc);// char * 转void *
cout<<*((char *)pv2)<<endl;输出字符‘1’
}
dynamic_cast
用法:dynamic_cast<type-id>(exdivssion)
,该运算符把exdivssion转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *。(type-id是类指针类型,exdivssion也必须是一个指针;type-id是一个引用,那么exdivssion也必须是一个引用)
- 主要作用于父类和子类之间指针或引用的转换。(dynamic_cast具有类型检查的功能,比static_cast 更安全)
- 同样可以作用于类之间的转换。
/*如果基类中不含虚函数,dynamic_cast 下行转换编译会出错*/
class father
{
public:
void fun1()
{
cout<<"this is father fun1 call\n";
}
virtual void fun()
{
cout<<"this is father fun call\n";
}
};
class son: public father
{
public:
void fun2()
{
cout<<"this is son fun2 call\n";
}
void fun()
{
cout<<"this is the son fun call\n";
}
int k;
};
int main()
{
father *pf, f;
son *ps, s;
pf = &f;// 父类的指针指向父类对象
ps = static_cast<son *>(pf);//这种转换是不安全的,行为是不确定的
if(ps != NULL)
{
ps->fun(); //在本文编译环境下,执行父类的fun
ps->fun2();
ps->k = 1;
}
ps = dynamic_cast<son *>(pf);//转换后ps = NULL
if(ps == NULL)
cout<<"dynamic_cast: ps = NULL\n";
cout<<"\n------------------------------------------\n";
pf = &s; //基类指针开始指向子类对象
//此时,两种转换都是安全的
ps = static_cast<son *>(pf);
if(ps != NULL)
{
ps->fun();
ps->fun2();
ps->k = 1;
}
ps = dynamic_cast<son *>(pf);//转换后ps = NULL
if(ps != NULL)
{
ps->fun();
ps->fun2();
ps->k = 2;
}
}
/*上行转换*/
int main()
{
father *pf, f;
son *ps, s;
ps = &s;// 子类的指针指向子类对象
//此时两种转换都是安全的
pf = static_cast<father *>(ps);
if(pf != NULL)
{
pf->fun();
}
pf = dynamic_cast<father *>(ps);
if(pf != NULL)
{
pf->fun();
}
}
reinterpret_cast
用法:reinterpret_cast<type-id>(exdivssion)
。reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。
- 从指针类型到一个足够大的整数类型
- 从整数类型或者枚举类型到指针类型
- 从一个指向函数的指针到另一个不同类型的指向函数的指针
- 从一个指向对象的指针到另一个不同类型的指向对象的指针
- 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
- 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针
总结:reinterpret_cast用在任意指针(或引用)类型之间的转换;以及指针与足够大的整数类型之间的转换;从整数类型(包括枚举类型)到指针类型,无视大小。
int main()
{
int a = 49;
int *pi = &a;
char *pc = reinterpret_cast<char*>(pi);//int * 到char *,用户自己安全
cout<<*pc<<endl;
unsigned long b = reinterpret_cast<unsigned long>(pc);//char * 转 unsigned long
cout<<b<<endl;//输出pc指向地址(即a的地址)对应的整数
int *pi2 = reinterpret_cast<int *>(b);//unsigned long 转 int*
cout<<*pi2<<endl;
}
const_cast
const_cast<type-id>(exdivssion)
;该运算符用来修改类型的const、volatile、__unaligned属性。(被const 、volatile、__unaligned修饰, type_id和exdivssion的类型是一样的)。
常量指针被转化成非常量指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
int main()
{
const int a = 100;
int *b = const_cast<int *>(&a);//const int * 转int *
cout<<*b<<endl; //输出100
cout<<&a<<" "<<b<<endl; //两者值相同,表明b指向a的地址,只是const属性变了
}
总结:
类指针或引用的上行转换static_cast 和 dynamic_cast 都可以
类指针或引用的下行转换用dynamic_cast并且判断转换后是否为空
基本数据类型之间的转换用static_cast, 但是由于数值范围的不同,需要用户保证转换的安全性
不同类型之间的指针或引用的转换用reinterpret_cast,它的本质是对指向内存的比特位的重解释
消除数据的const、volatile、__unaligned属性,用const_cast