目录
一、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语言编写顺序表中的坑:
在insert函数中,我们在 i 位置进行插入数据。
//在i位置插入数据
void Insert(int* arr, size_t& arr_size, size_t i, int val)
{
int end = arr_size - 1;
while (end >= i)
{
arr[end + 1] = arr[end];
end--;
}
arr[i] = val;
arr_size++;
}
void test01()
{
//大小为10的int数组
int* arr = (int*)malloc(sizeof(int) * 10);
size_t arr_size = 5;
//初始化数组
arr[0] = 1,arr[1] = 2,arr[2] = 3,arr[3] = 4,arr[4] = 5;
//插入数据
Insert(arr, arr_size, 0, 70);
//打印数组
for (int i = 0; i < arr_size; i++)
printf("%d ", arr[i]);
}
当我们运行代码,程序便出现了运行超时,即死循环。
原因:
因为当我们在0位置插入数据的时候,end-- 移动数据后会等于-1,当 end== -1时,进行end >= i 判断时,因为 i 是size_t 类型,所以end会隐式类型转化为 size_t 类型,而size_t 类型的 -1 为全1,是无符号数的最大值,则会进行死循环。
类似这样的坑在C语言类型中经常出现。
因此C++提出了自己的类型转化风格,因为C++要兼容C语言,所以C++中还是可以使用C语言的转化风格。
二、C++强制类型转换
static_cast 、 reinterpret_cast 、 const_cast 、 dynamic_cast
2.1 static_cast
使用static_cast用于费多态类型的转换,编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。
2.2 reinterpret_cast
2.3 const_cast
原因分析:
a被const修饰,则编译器会认为其不会被修改,则会将其加载至寄存器中,当我们使用指针p获取a的地址时,则会去内存中取出a的地址,赋值给a。当我们解引用修改a地址处的值时,a内存中的值就会被修改;而让我们直接打印a的值时,这是一种读操作,编译器会直接回去寄存器中取出a的值,而不是内存中,则打印出2;当我们使用p解引用则会去内存中取出a的值,则会打印3。
当我们使用监视窗口时,会重新创建一个进程,该进程在获取数据时则会去内存中读取,则读取到的a值是3.
我们可以使用 volatile关键字 修改const int a 让编译器不进行优化。
其实也可以使用(int*)进行强制转换。
此种const情况,使用reinterpret_cast也不行,必须使用const_cast。
2.4 dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
- 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
- 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
-
dynamic_cast只能用于父类含有虚函数的类
-
dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
首先我们要明确,父类是无论如何不能转为子类的。
而指针和引用是要允许进行转换的,因为父类指针可以指向父类也可以指向子类。
具体看下面这个例子,pa 指针可能指向父类也可能指向子类。
class A
{
public:
virtual void f(){}
int _a = 0;
};
class B : public A
{
public:
int _b;
};
// pa 可能指向父类也可能指向子类
void fun(A* pa)
{
//函数体……
}
void test06()
{
A aa;
B bb;
fun(&aa);
fun(&bb);
}
当 pa 指向父类时,我们将其强行转化为子类的指针,该指针则会指向非对象的空间,出现越界的风险。
如果 pa 指向子类对象,我们是无法访问到子类的成员的。
此时我们进行要将pa转换为子类指针才能进行访问:
通过以上例子的分析,我们得出两个痛点:
- 当指针指向父类对象时,我们不能将其转换为子类指针,要不然会出现越界。
- 当指针指向子类对象时,我们要将其转换为子类指针,才能访问到子类的成员。
而dynamic_cast关键字就解决了以上两个痛点:
dynamic_cast的作用是将父类对象的指针(引用)向子类对象指针(引用)进行转换;如果当前指针指向父类,则其不能将转换为子类指针,如果当前指针指向子类对象,则其可以转换为子类指针。
注意:dynamic_cast 只能用于 父类 +虚函数的结构。即多态类型,没有虚函数编译直接会报错。而强制类型转换是对虚函数是没有要求,可以直接进行转换的。
三、RTTI
RTTI : Run-time Type identifification 的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
-
typeid运算符 —— (获取对象类型字符串)
-
dynamic_cast运算符 —— (继承关系)
-
decltype —— (推导对象类型用于定义另一个对象)