C++在兼容C的强制类型转换后,提出了自己的类型转换模式
一、C语言中的类型转换
在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不陪陪,或者返回值类型与接收返回值类型不一致时,就需要发生类型转换
C语言有两种类型的转换:隐式类型转换和显式类型转换
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,隐式类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告
无论是隐式类型转换还是显示类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值
void Test()
{
int i = 1;
// 隐式类型转换
double d = i;
printf("%d, %.2f\n", i, d);
int* p = &i;
// 显示的强制类型转换
int address = (int)p;
printf("%x, %d\n", p, address);
}
二、C++的类型转换
C语言风格的转换格式很简单,但存在如下缺点:①隐式类型转换导致数据精度丢失②显示类型转换混杂所有情况,代码不够清晰
因此C++提供了四种类型转换
1.static_cast
用于非多态类型的转换,任何编译器执行的隐式类型转换都可以使用
static_cast必须用于相关类型的转换
static_cast是编译时静态类型检查,使用static_cast可以尽量发挥编译器的静态类型检查功能,但是并不能保证代码一定“正确”
void test_1()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
}
2.reinterpret_cast
用于为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种类型
一般多用于指针转换
void test_2()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
// 这里使用static_cast会报错,应该使用reinterpret_cast
//int *p = static_cast<int*>(a);
int* p = reinterpret_cast<int*>(a);
}
3.const_cast
用于改变变量的const属性或volatile属性
Volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适。volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。因此再增加volatile关键字,下面的代码中就可以进行修改
const_cast为什么能修改const为非const?
因为const变量不是物理上(将变量写在只读内存区)实现的,而是编译的时候由编译器保证不被修改的
void test_3()
{
const int a = 2;
int* p = const_cast<int*>(&a);
//int* p = (int*)&a;//也可以实现
*p = 3;
cout << a << endl;//2
//编译器实际上对a进行了修改,但是由于const限制,编译器对a进行优化,阻止a的改变
cout << *p << endl;
//volatile const int a = 2;
//int* p = const_cast<int*>(&a);
//*p = 3;
//cout << a << endl;//3
//cout << *p << endl;
}
4.dynamic_cast
dynamic的意思是动态的。dynamic_cast也就是动态转换,将父类对象的指针/引用转换为子类对象的指针或引用
向上转型:子类对象的指针/引用->父类指针/引用(不需要使用,子类继承父类,兼容父类)
向下转型:父类对象的指针/引用->子类指针/引用(用dynamic_cast转型时安全的)
注意:①dynamic_cast只能用于父类含有虚函数的类
②dynamic_cast会先检查能否转换成功,如果能则转换,不能则返回0
class A
{
public:
virtual void f() {}
};
class B : public A
{};
void func(A* pa,const string& s)
{
cout << "pa指向" << s << endl;
B* pb1 = (B*)pa;//不安全的
B* pb2 = dynamic_cast<B*>(pa);//安全的
cout << "强制转换\tpb1:" << pb1 << endl;
cout << "dynamic_cast\tpb2:" << pb2 << endl;
}
void test_4()//dynamic_cast动态转换
{
A a;
B b;
func(&a, "指向父类对象");
func(&b, "指向子类对象");
}
三、RTTI
RTTI(Run-Time Type Identification)-运行时类型识别,使程序能够获取由指针或引用指向对象的实际派生类型
C++为了支持RTTI,提供了dynamic_cast、typeid、decltype三种方法
1.typeid
typeid会将获取到的类型信息保存到一个type_info类型对象中,并返回该对象的const引用。如果需要具体的类型信息,可以通过成员函数来提取
为了减小编译后文件的体积,编译器不会为所有类型创建type_info对象,只会为使用了typeid运算符的类型创建。只有带虚函数的类,不论是否使用typeid运算符,编译器都会创建type_info对象
typeid是操作符,而不是函数
#include<iostream>
#include<typeinfo>//需要添加的头文件
using namespace std;
void main()
{
//typeid().name()可以返回变量、函数、类的数据类型名,功能是相当强大的
//注意:对非引用类型,typeid().name()是在编译时期识别的,只有引用类型才会在运行时识别
const int a = 10;
cout << typeid(&a).name() << endl;//const int *
cout << typeid(typeid(a).name()).name() << endl;
//结果为char const *,因此typeid().name()返回了存储类型名的字符串。
}
注意:不可以使用typeid().name()作为变量类型
2.decltype
decltype是一种类型说明符,目的是解决复杂类型的什么。decltype并不计算表达式的值,而是通过编译器分析表达式并得到它的类型
①作用于变量直接得到变量的类型;②作用于表达式,结果是左值的表达式得到类型的引用,结果是右值的表达式得到类型;③作用于函数名会得到函数类型,不会自动转换成指针。
const int ci = 0, &cj = ci;
// x的类型是const int
decltype(ci) x = 0;
// y的类型是const int &
decltype(cj) y = x;