C++中的类型转换相较于C语言多了4种显示转换和对象之间的转换,一起来看看吧~
隐式类型转换
隐式类型转换,顾名思义,就是数据之间的类型可以悄咪咪的自动进行转换,这里的数据类型指的是基础数据类型。基础数据类型之间都可以进行隐式类型转换,比如char 和 short 会自动转换为int;任意基础数据类型的指针Type *都可以隐式转换为void * 类型的指针;继承中的子类指针可以隐式转换为父类类型的指针,子类的引用可以隐式转化为父类类型的引用
显示类型转换
强制类型转换
- 用法:(目标类型)源对象
- 说明:强制类型转换虽然能够转换类型,但转换后的结果可能不正确,比如double类型的数据强转为int类型的数据会丢失部分数据;不同类型的指针和引用强转得到的地址可能也不相同
静态类型转换
- 用法:static_cast<目标类型>(源对象)
- 使用场景:适用于源对象类型和目标类型双方在一个方向上可以进行隐式类型转换,则两个方向上都可以进行静态类型转换
int n = 1024;
void *pv = &n;
// 这里的int* 会隐式转化为 void *
int *pn = static_case<int *>(pv);
// char 会隐式转化为 int
char c = static_cast<char>(n);
n = static_cast<int>(c);
struct A{
int a;
};
//继承
struct B:public A{
int b;
};
struct C:public A{
int c;
};
B b;
// 子类引用会隐式转化为父类的引用
A& ra = b;//隐式类型转换
B& rb = static_cast<B&>(ra);
去常属性类型转换
- 用法:const_cast<目标类型>(源对象)
- 使用场景:适用于去除常指针或常引用的常属性
const int n = 1024;
// 可以去除int * 的const属性
int *p = const_cast<int *>(&n);
const int &a = n;
// 可以去除int & 的const属性
int &r = const_cast<int &>(a);
重解释类型转换
- 用法:reinterpret_cast<目标类型>(源对象)
- 使用场景:适用于不同类型的指针之间或者整数与指针之间的类型转换
int n = 0x12345678
// int * 重解释为 char *
char *p = reinterpret_cast<char *>(&n);
void *pv = reinterpret_cast<void *>(n);
// void * 重解释为 int 类型
int m = reinterpret_cast<int>(pv);
动态类型转换
- 用法:dynamic_cast<目标类型>(源对象)
- 使用场景:适用于有多态关系的父子指针或者引用类型之间的转换
- 说明:转换过程会校验源对象和目标类型是否一致,只有类型一致才转换成功,否则返回NULL;而静态类型转换、重解释类型转换和强制转换不会校验源对象是否能够转换成目标对象,要避免使用这三种类型来转换存在多态的父子指针或者引用
class A{
public:
virtual void func(){
// 虚函数,多态
}
};
class B:public A{
public:
};
class C:public A{
public:
}
int main()
{
A *pa = new B; // A父类指针指向B类子对象
B *pb;
C *pc;
cout << "dynamic_cast<>" << endl;
pb = dynamic_cast<B*>(pa); // OK
cout << pb << endl;
pc = dynamic_cast<C*>(pa); //返回NULL,会校验类型
cout << pc << endl;
cout << "static_cast<>" << endl;
pb = static_cast<B*>(pa);
cout << pb << endl;
pc = static_cast<C*>(pa); // 不会校验类型,但有问题,得到的地址与pb一样,显然转换失败
cout << pc << endl;
cout << "reinterpret_cast<>" << endl;
pb = reinterpret_cast<B*>(pa);
cout << pb << endl;
pc = reinterpret_cast<C*>(pa); // 不会校验类型,但有问题,得到的地址与pb一样
cout << pc << endl;
cout << "force" << endl;
pb = (B*)(pa);
cout << pb << endl;
pc = (C*)pa; // 不会校验类型,但有问题,得到的地址与pb一样
cout << pc << endl;
}
小插曲:我们知道在用父类指针指向子类对象时,事实上父类指针指向的是子类对象中的父类类型的基类子对象,和真正的子类对象的地址会存在一定偏移 ,子类类型的指针可以隐式转化为父类类型指针,但是父类类型的指针是无法隐式转化为子类类型的指针的,那么怎样可以正确得到子类对象的地址呢?
1、因为父类指针和子类指针之间存在隐式转换,所以可以用静态类型转换static_cast<>,能够正确计算出子类类型对象的起始地址
2、也可以用强制类型转换(*SON),也能够计算出子类类型对象的起始地址
3、但重解释类型转换reinterpret_cast<>不能得到正确的子类对象的地址,只发生类型改变,地址不会偏移
对象之间的类型转换
-
如果有A类型对象a和B类型对象b,如何让a对象转换为b对象呢?
1、如果A和B都是基础数据类型,则a自动转换为b
2、如果A是基础数据类型,而B是类类型,则需在B类型中提供A类型的单参构造函数
这里提一下单参构造函数的定义:如果一个类A提供了一个T类型的单参构造函数,那么T类型的数据可以隐式调用该单参构造函数转化为A类型的对象
class A{
private:
int x;
string name;
int score;
public:
A(int x):x(x){
// 单参构造函数,能被形参类型相同的数据隐式调用
}
A(string name):name(name){
// 转换构造函数
}
A(int x,string name):x(x),name(name){
// C++11 中可以对两个参数的构造函数对于形参类型相同的数据进行隐式调用
}
};
int main()
{
A a(10);
A b("mark");
a=11; // 会调用 A(int),11会隐式转换为A类型的对象
b=string("alice"); // 会调用 A(string),string会隐式转换为A类型的对象
}
3、如果A是类类型,B是基础数据类型,则需在A类型中重载B类型运算符
这里又得提一下类型运算符的定义:如果在X类中重载TYPE类型运算符,则X类型的对象都可以隐式调用重载的类型函数转化为TYPE类型的对象
class X{
//在X类中重载TYPE类型运算符
operator TYPE(void){
return TYPE类型对象;
}
};
class Emp{
public:
Emp(int no=0,string name="",float s=0):no(no),name(name),s(s){
}
/*
Emp(const Stu& s){
cout << "construct" << endl;
}
*/
public:
int no;
string name;
float s;
};
class Stu{
public:
Stu(int no=0,string name="",int s=0):no(no),name(name),s(s){
cout << "Stu" << endl;
}
Stu(const Emp& e){
cout << "construct" << endl;
}
//重载 int 类型运算符
operator int(void){
return no;
}
operator string(void){
return name;
}
operator Emp(void){
cout << "operator" << endl;
return Emp(no,name);
}
/*
friend operator Emp(const Stu& s){
return Emp(s.no,s.name);
}
*/
public:
int no;
string name;
int s;
};
int main()
{
Stu s = 110;
cout << s << endl;//隐式调用operator int函数转换为了int
int n = s;
cout << n << endl;
Stu s1(120,"jack",99);
string m = s1;
cout << m << endl;
Emp e = s;//Stu --> Emp
}
4、如果A类型和B类型都是类类型,则可以在A类型中重载B类型运算符,也可以在B类型中提供A类型的构造函数