前言
类型转换用于数据类型的转换,分为旧式类型转换和新式类型转换。
一、旧式类型转换
C语言中可以用(type)value进行旧式类型转换,如double(a),double(12);C++中增加了type(value)的写法,但为了兼容C,一般不用type(value)的写法。
将整型变量a转换为双精度浮点型输出:
#include<iostream>
#include<stdio.h>
using namespace std;
int main()
{
int a;
a=12;
printf("%f\n",a);
printf("%f\n",(double)a);
printf("%f\n",double(a));
return 0;
}
输出:
二、新式类型转换
本质上跟旧式类型转换没有区别,只不过将转换的数据划分得更细了,也就是A种类型的数据转换只能用A种类型的数据转换。新式类型转换一共有以下四种:
static_cast
const_cast
dynamic_cast
reinterpret_cast
在进行测试之前首先要定义三个类:
#include<iostream>
#include<stdio.h>
using namespace std;
class CFather{
public:
int a;
};
class CSon:public CFather{
public:
int b;
};
class COther{
public:
double c;
};
1.static_cast
形式:static_cast<type>(expression)
说明:当type和expression可以互相隐式转换的时候,这个方法执行起来才是合法的
什么是隐式转换?
就是可以直接用等于号,进行赋值后会自动转换。
下面的o不能直接转化为f.
下面的f不能转化为s
下面的的s能转化为f,也就是父类指针能指向子类空间:
使用旧式类型转换:
int main()
{
CFather* f;
CSon* s=new CSon;
COther *o=new COther;
cout<<o->c<<endl;
f=(CFather*)s;
s->a=1;
s->b=2;
cout<<f->a<<endl;
o=(COther*)s;
cout<<o->c<<endl;
return 0;
}
执行结果:
不报错
使用static_cast将s转换成o:
会报错。
int main()
{
CFather* f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s->a=1;
s->b=2;
f->a=3;
s=static_cast<CSon*>(f);
cout<<s->b<<endl;
f=static_cast<CFather*>(s);
s->b=5;
s->a=6;
cout<<s->b<<endl;
cout<<f->a<<endl;
return 0;
}
输出:
从上面的代码和运行结果可以看出:static_cast<>()可以用于父类和子类的相互转换,但不可以用在不相关类之间的转换,否则会报错。
2.const_cast
形式:const_cast<type>(expression)
说明:用于去掉表达式的const或volatile属性,仅当type和expression一样的时候,才合法。
对于指针常量来说,指针所指向的那块内存的值是不可以改变的。
执行结果:
当使用不安全的类型转换时:
int main()
{
CFather const *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=(CSon*)f;
s->a=5;
cout<<f->a<<endl;
return 0;
}
执行结果:
使用强制类型转换时,借助一个非指针常量可以修常指针常量的内存,破坏了const不可修改的规则。
于是就有了const_cast.
const_cast的type和expression类型不匹配时,会报错。
非const指针不能直接指向const内存
static_cast无法去掉f的const属性
int main()
{
CFather const *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
CFather *f1=const_cast<CFather*>(f);
f1->a=6;
cout<<f->a<<endl;
return 0;
}
用const_cast可以让非const属性的指针指向const属性的内存,并对const属性的内存修改,但是expression的const属性并没有失效.
那这种做法跟强制类型转换有什么区别呢?
本质上没有区别,但是这种做法增加了程序的可读性,而且也防止了不安全的类型转换。
3.dynamic_cast
形式:dynamic_cast<type>expression.
说明:用于父子类型的转换。子类型可以直接转换成父类型,但是父类型要转换成子类型的时候,需要符合多态条件。如果是不相关类型的转换,会报错。
子类可以直接转换成父类对象:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
f=dynamic_cast<CFather*>(s);
s->a=11;
cout<<f->a<<endl;
return 0;
}
实验结果:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
f=dynamic_cast<CSon*>(s);
s->a=10;
cout<<f->a<<endl;
return 0;
}
执行结果:
不相关的类型转换,会报错:
父类转换成子类,需要符合多态条件:
多态情况:1.在子类中添加一个虚函数,但父类中不添加:
class CSon:public CFather{ public: int b; virtual int add(int a,int b){return a+b;} };
还是会报错。2.在父类中添加一个虚函数,但子类中不添加:
class CFather{ public: int a; virtual int add(int a,int b){return a+b;} };
然后下面的代码不报错了:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=dynamic_cast<CSon*>(f);
s->a=12;
cout<<f->a<<endl;
return 0;
}
但是运行会出错:
修改一下代码:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=dynamic_cast<CSon*>(f);
f->a=12;
cout<<s->a<<endl;
return 0;
}
还是运行失败:
再改一下:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=dynamic_cast<CSon*>(f);
f->a=12;
cout<<f->a<<endl;
return 0;
}
运行成功:
使用强制类型转换:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=(CSon*)f;
s->a=13;
cout<<f->a<<endl;
return 0;
}
运行成功:
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
cout<<sizeof(*f)<<endl;
cout<<sizeof(*s)<<endl;
s=dynamic_cast<CSon*>(f);
cout<<sizeof(*f)<<endl;
cout<<sizeof(*s)<<endl;
cout<<s->a<<endl;
return 0;
}
输出结果:
说明了用dynamic_cast即使父类对象转换为了子类对象,子类指针也无法访问父类对象成员。
4.reinterpret_cast
形式:reinterpret_cast<type>(expression)
说明:用于危险的类型转换
struct dat{
short a;
short b;
};
int main()
{
long value=0xA224B118;
dat* p=reinterpret_cast<dat*>(&value);
cout<<hex<<p->a<<endl;
cout<<p->b<<endl;
return 0;
}
执行结果:
缺点:不可移植。
补充
#include<iostream>
#include<stdio.h>
using namespace std;
class CFather{
public:
int a;
virtual int add(int c,int d){return c+d;}
};
class CSon:public CFather{
public:
int b;
int add1(int c,int d){return c+d;}
};
class COther{
public:
double c;
};
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
s=(CSon*)f;
cout<<s->add1(3,4)<<endl;
return 0;
}
实验结果:
#include<iostream>
#include<stdio.h>
using namespace std;
class CFather{
public:
int a;
virtual int add(int c,int d){return c+d;}
};
class CSon:public CFather{
public:
int b;
int f;
int add1(int c,int d){return c+d;}
};
class COther{
public:
double c;
};
int main()
{
CFather *f=new CFather;
CSon* s=new CSon;
COther *o=new COther;
cout<<sizeof(*s)<<endl;
cout<<sizeof(*f)<<endl;
cout<<f<<endl;
cout<<&(s->f)<<endl;
cout<<sizeof(*f)<<endl;
s=(CSon*)f;
cout<<f<<endl;
cout<<&(f->a)<<endl;
cout<<&(s->f)<<endl;
s->f=5;
cout<<sizeof(*f)<<endl;
cout<<sizeof(*s)<<endl;
cout<<s->f<<endl;
return 0;
}
执行结果:
输出解释:
&(s->a)之前有一个虚函数表指针,占八个字节。
&(s->f)和&(s->a)之间有一个&(s->b);
说明:父类对象强行转换为子类对象时,父类对象会根据子类对象的大小进行扩充
总结
static_cast相对来说比较重要,需要重点掌握。