C风格的类型转换
C风格类型转换包括隐式和显式(强制)。如int a = 2.5
或float a = (float)1
等,针对赋值、函数调用、返回值等情况,常见类型可以进行自动的类型转换,转换方式通常是有意义的,例如浮点型转换为整形,会截掉浮点部分,char型转换成整形会按照ASCII码排序等,隐式类型转换通常按照提升原则,把较小的类型如char提升到较大的类型如int再做进一步操作。
static_cast
类似C风格的静态类型强制转换,适用于:
- 基础类型转换
- 基类转子类(不安全),子类转基类(安全)
- 任意表达式转void指针
int a = 0;
int b = static_cast<int>(2.5); // 正确,基础类型转换
int *c = &a;
float *d = static_cast<float*>(c); // 错误,无关类型转换
void *e = static_cast<void*>(c); // 正确,任意类型转空指针
上面提到了静态类型和动态类型,一个对象(变量)的静态类型就是其声明类型,如表达式int a中的int就是对象a的声明类型,即静态类型;而一个对象(变量)的动态类型就是指程序执行过程中对象(指针或引用)实际所指对象的类型,如Base* pB = new Drived; 其中class Drived继承于class Base,则指针对象pB的静态类型就是Base(声明类型),动态类型就是Drived(实际所指对象的类型),又如Base* pB = new Base;此时指针对象pB的静态类型和动态类型也是相等的,都是Base。对于动态类型我们需要下面的dynamic_cast。
dynamic_cast
有条件的动态类型转换,适用于:
- 基类和子类之间的安全转换(基类必须要有虚函数)
- 相同基类不同子类的类型转换
class BaseClass {
public:
virtual void foo() {}; //基类必须有虚函数,否则dynamic_cast编译错误
};
class DerivedClass : public BaseClass {
};
BaseClass* pb = new DerivedClass();
DerivedClass *pd1 = static_cast<DerivedClass *>(pb); //子类->父类,静态类型转换,正确但不推荐
DerivedClass *pd2 = dynamic_cast<DerivedClass *>(pb); //子类->父类,动态类型转换,正确
BaseClass* pb2 = new BaseClass();
DerivedClass *pd21 = static_cast<DerivedClass *>(pb2); //父类->子类,静态类型转换,危险
DerivedClass *pd22 = dynamic_cast<DerivedClass *>(pb2); //父类->子类,动态类型转换,结果是NULL
reinterpret_cast
用来处理无关类型间的转换,随便搞,编译都没问题,但是后面不一定能用。适用于:
- 整形和指针间
- 函数指针之间
- 对象指针之间
- 类成员函数指针之间
- 类成员变量指针之间
直观上reinterpret_cast好像没什么用,因为转换类型过后大概率不能用,还是得转回原先的类型才能用。下面是一个很直(wu)观(liao)的例子。
#include <iostream>
// 返回一个地址的哈希值
// 不必在意地址所存放的具体类型和含义
unsigned short Hash(void *p) {
unsigned int val = reinterpret_cast<unsigned int>(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;
}
const_cast
const_cast用于去除变量的常量(只读)属性,也是唯一可以达到这个目的的转换方式。例如:
int a = 1;
const int* b = &a;
*b = 2; // 错误,const对象
int *c = const_cast<int *>(b);
*c = 3; // 正确,const已解除
类型转换运算符
类成员的重载运算符,类似operator int();
的形式,隐式类型转换时会调用,跟上面的几个关系不大,在这里就mark一下,有机会拓展。
网友评论