[C++] C、C++类型转换
标题:[C++] C、C++类型转换
@水墨不写bug
目录
一、C的类型转换
二、C++的类型转换
1.static_cast(对应隐式类型转换)
2.reinterpret_cast(对应强制类型转换)
3.const_cast(对应强制类型转换中有风险的去掉const属性)
4.dynamic_cast
正文开始:
一、C的类型转换
C++的类型转换是对C语言类型转换的规范。
在C语言中,发生的类型转换有两种:隐式类型转换和显示类型转换;如果赋值符号左右两侧的类型不同,或者形参与实参类型不匹配,或者返回值类型与接受返回值类型不一致时,就会发生类型转换。
隐式类型转换
隐式类型转换编译器在编译阶段自动转换,如果可以转换,则没有提醒;如果不能转换,则编译失败。
显示类型转换
需要我们自己手动处理,如用到“()”的强制类型转换。
C语言的隐式类型转换我们在C语言阶段学习过,它遵循一定的规则,这里我们举一些例子:
bool与整形的相互转换:非0的任意整形为真true,0为假false;true转为整形为1,false转为整形为0;
浮点型与整形的相互转换:浮点数赋值给整形,只做近似处理。结果仅仅保留浮点数小数点之前的整数部分;整数赋值给浮点数,小数部分记为0,整数部分保留。
无符号类型的超范围赋值:如果给一个无符号类型赋一个超出范围的值,结果是对无符号类型表示数值总数取模后的余数。
有符号类型的超范围赋值:给有符号类型超范围赋值,结果未定义!
除此之外,内置类型与自定义类型之间相互转换 :内置类型可以通过构造函数隐式类型转换为自定义类型:单参数构造函数支持直接赋值,多参数构造函数支持用“{}”花括号赋值。
#include<iostream>
using namespace std;
class A{public:A(int a = -1):_a(a),_b(a){}int _a;int _b;
};
class AA{public:AA(int a1 = -1,int a2 = -1):_a(a1), _b(a2){}int _a;int _b;
};
int main()
{A aa1 = 1;//单参数构造函数支持直接用内置类型隐式类型转换AA aa2 = { 1,2 };//多参数构造函数可以使用花括号实现隐式类型转换return 0;
}
自定义类型也可以隐式类型转换为内置类型,需要通过特定的成员函数实现:
这个成员函数是一个有着特殊实现语法的函数,实现的语法格式如下:
operator /*需要转换成的类型*/()
{//具体的实现没有限制,可以对这个对象内部的数据进行运算,最终返回结果即可return /*返回需要转换成的类型即可*/
}
比如,实现将下面这个类型隐式类型转换为double:
class A
{
public:int _a;double _b;
};
那么就需要实现这样的一个函数:(函数内部如何实现没有限制,只要实现了A到double类型的转换即可)
operator double(){return _a + _b;}
于是,可以进行A到double的隐式类型转换了:
class A
{
public:A(int a = -1,double b = -1):_a(a),_b(b){}operator double(){return _a + _b;}int _a;double _b;
};int main()
{A aa1(4, 8.7);double f = aa1;//f 的值为 12.7return 0;
}
自定义类型和自定义类型也是可以实现相互转换的,本质就是借助对应的构造函数:
class A{
public:A(int a = -1,int b = -1):_a(a),_b(b){}const int get() const{return _a + _b;}int get(){return _a + _b;}
private:int _a;int _b;
};
class B{
public:B(int c = -1):_c(c){}B(const A& aa):_c(aa.get()){}int _c;
};
int main()
{A aa = { 2,5 };B bb;bb = aa;//B提供了从A构造B的构造函数,所以可以实现A隐式类型转换到Bcout << bb._c << endl;// 7return 0;
}
强制类型转换也只是能将有一定关系的类型相互强制转换。相反,如果两个类型相差十分大,强制类型转换也无能为力。
缺陷:
转换的可视性比较差,所有的转换形式都是以一种相同的形式书写,难以跟踪错误的转换。
二、C++的类型转换
注意C++是兼容C的,所以在C++程序中,也会出现C的类型转换。由于C的类型转换混合杂糅,难以区分,十分模糊,所以C++提出了自己的一套类型转换逻辑。
这些类型转换是需要显示使用的,所以在掌控之中的类型转换时,用C++的类型转换能够使源码的逻辑性,可读性大大提高。标准C++为了使类型转换可视化,引入了四种类型转换操作符:
static_cast
reinterpret_cast
const_cast
dynamic_cast
这些类型转换操作符的使用方法如下:
1.static_cast(对应隐式类型转换)
static_cast用于非多态类型的转换(静态转换),编译器进行任何类型的隐式类型转换都可以用static_cast,但是不能用于两个不相关类型转换。
原来可以隐式类型转换的,就可以使用static_cast进行转换。
int main()
{int a = 4;double b = static_cast<int>(a);cout << a << " " << b << endl;cout << "------------------" << endl;double d = 4.98;int f = static_cast<int>(d);cout << d << " " << f << endl;return 0;
}
4 4
------------------
4.98 4
两种类型相差过大,根本不相关,无法类型转换:
class A
{int _a;
};
class B
{double _b;
};
int main()
{A a;B b = static_cast<A>(a);return 0;
}
2.reinterpret_cast(对应强制类型转换)
原来需要进行强制类型转换的,需要用reinterpret_cast。
比如:指针与整形的转换(数据的意义已经改变)
3.const_cast(对应强制类型转换中有风险的去掉const属性)
const_cast最常用的用途就是删除变量的const属性,方便赋值。
void Test ()
{const int a = 2;int* p = const_cast< int*>(&a );*p = 3;cout<<a <<endl;cout<<*p<<endl;
}
2
3
当我们运行上面这一段代码,就会发现两次输出是不同的,a和*p代表的值相同,但是为什么输出的值不同?
原因在于编译器的优化,由于识别a为const,所以直接把a的副本当作const对待了,当a改变的时候,a的副本没有改变,所以输出a(其实是a的副本)仍然是原先的2,而*p(a本身)则已经改变 为3。
如果想要不优化,需要在a的前面加上volatile关键字即可。
void Test()
{volatile const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << endl;cout << *p << endl;
}
int main()
{Test();
}
3
3
4.dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换):
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
dynamic_cast
在运行时检查类型信息(RTTI,即运行时类型信息),以确保转换的安全性。如果转换是安全的(即基类指针或引用确实指向了一个派生类的对象),则转换成功;如果不是,则转换失败。对于指针类型的转换,失败时返回 nullptr
;对于引用类型的转换,如果转换不安全,则会抛出一个 std::bad_cast
异常(但这通常不推荐用于引用类型的 dynamic_cast
,因为异常可能导致程序终止)。
注意:
1. dynamic_cast只能用于父类含有虚函数的类
2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0;
完 ~
未经作者同意禁止转载