标题:[C++] C、C++类型转换
@水墨不写bug
目录
3.const_cast(对应强制类型转换中有风险的去掉const属性)
正文开始:
一、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.7
return 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隐式类型转换到B
cout << bb._c << endl;
// 7
return 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;
完 ~
未经作者同意禁止转载