27.C++的类型转换(介绍了static_cast、reinterpret_cast、const_cast、dynamic_cast的用法)

1. C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换

  1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败

  2. 显式类型转化:需要用户自己处理

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风格的转换格式很简单,但是有不少缺点的:

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失

  2. 显式类型转换将所有情况混合在一起,代码不够清晰

    因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

2. C++强制类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:

static_cast、reinterpret_cast、const_cast、dynamic_cast

2.1 static_cast、reinterpret_cast、const_cast

#include<iostream>

using namespace std;

int main()
{
    // 情况1:
	int i = 1;
	// C++规范转换 -- static_cast适用相似类型的转换(这些类型的表示意义差不多)
    // 如下,将int类型转化为double类型
	double d = static_cast<double>(i);
	printf("%d, %.2f\n", i, d);
	// 打印结果为
	// 1
	// 1.00

    
    // 情况2:
	int* p = &i;
	// int* 是地址, int是整型,因此不是相似类型,也就不能够使用static_cast
	// C++规范转换 --reinterpret_cast适用于不相关的类型之间的转换
	// reinterpret v.重新解释
    // 如下将int*类型转化为int类型
	int address = reinterpret_cast<int>(p);
	printf("%x, %d\n", p, address);
	// 打印结果为
	// 56f9a8
	// 5700008


    // 情况3:
	// C++规范转换 -- const_cast 去掉const属性。单独分出来,警示你这个很危险,用的时候谨慎一点
	const int a = 2;
    // 如下,去掉了&a的const属性
	int* p1 = const_cast<int*>(&a);
	*p1 = 3; // 将a内存中对应的值修改为3
	cout << a << endl;   
	cout << *p1 << endl;
	// 打印结果为
	// 2
	// 3
	// 为什么a和*p1打印的结果不一致呢?
	// 这是因为a的类型为const int,编译器默认a是不会被改变的
	// 因此将a的值,直接拿到了寄存器来使用,不再到内存中读取a的值(这是一种编译器的优化),打印a时,打印的是寄存器中的值
	// 而*p1指向的是a内存中的位置,打印的a内存对应的值


    // 情况4:
	// 使用关键词volatile,编译器就不会进行优化,读取a时,会到内存中进行读取,而不是在寄存器中读取
	// volatile  /ˈvɑːlət(ə)l/  adj.易变的
    // 使用关键词修饰volatile,那么编译器就不会进行优化
	volatile const int a1 = 2;
	int* p2 = const_cast<int*>(&a1);
	*p2 = 3;
	cout << a1 << endl;   
	cout << *p2 << endl;
	// 打印结果为
	// 3
	// 3
    
	return 0;
}

2.2 dynamic_cast

dynamic_cast用于将一个基类对象的指针/引用转换为派生类对象的指针或引用(动态转换)

向上转型:派生类类对象指针/引用->基类指针/引用(不需要转换,赋值兼容规则)

向下转型:基类对象指针/引用->派生类指针/引用(用dynamic_cast转型是安全的)

注意:

  1. dynamic_cast只能用于基类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
  • 错误演示
#include<iostream>

using namespace std;

// 基类
class A
{
public:
	virtual void f() {}

	int _a = 0;
};


// 派生类
class B : public A
{
public:
	int _b = 0;
};


void Func(A* ptr)
{
	// 直接转换是不安全的
    // 当ptr指向的是派生类对象B时,那么派生类对象转为派生类对象是没有什么问题的
    // 当ptr指向的是基类对象A时,那么基类对象转为派生类对象存在越界风险
    // _b是属于派生类对象B,如果将基类对象转为子类对象,那么基类对象也可以访问到_b,这就造成了越界,是不安全的
	B* bptr = (B*)ptr;
	cout << bptr << endl;

	bptr->_a++;
	bptr->_b++;

	cout << bptr->_a << endl;
	cout << bptr->_b << endl;
}

int main()
{
	A aa;
	B bb;
	Func(&aa);
	Func(&bb);

	return 0;
}
  • 使用dynamic_cast进行转换
#include<iostream>
using namespace std;

// 基类
class A
{
public:
	virtual void f() {}

	int _a = 0;
};

// 派生类
class B : public A
{
public:
	int _b = 0;
};

void Func(A* ptr)
{
	// C++规范的dynamic_cast是安全的
	// 如果ptr是指向基类,则转换失败,返回空
	// 如果ptr是指向派生类,则转换成功

	B* bptr = dynamic_cast<B*>(ptr);
	cout << bptr << endl;

    // 当bptr不为空,说明是派生类转派生类,转换成功
    // 当bptr为空,说明是基类转派生类,转换失败,此处进行判断,避免越界,保证安全访问
	if (bptr)
	{
		bptr->_a++;
		bptr->_b++;

		cout << bptr->_a << endl;
		cout << bptr->_b << endl;
	}
}

int main()
{
	A aa;
	B bb;
	Func(&aa);
	Func(&bb);

	return 0;
}

相关推荐

  1. C++类型转换

    2024-05-15 13:22:13       45 阅读
  2. C++】C++类型转换

    2024-05-15 13:22:13       51 阅读
  3. C#中类型转换

    2024-05-15 13:22:13       58 阅读
  4. C++中类型转换

    2024-05-15 13:22:13       41 阅读
  5. Rust错误处理:`foo(...)?`与错误类型转换

    2024-05-15 13:22:13       42 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-15 13:22:13       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-15 13:22:13       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-15 13:22:13       82 阅读
  4. Python语言-面向对象

    2024-05-15 13:22:13       91 阅读

热门阅读

  1. 30、Flink 的故障恢复详解

    2024-05-15 13:22:13       22 阅读
  2. 三数之和算法题(LeetCode)

    2024-05-15 13:22:13       29 阅读
  3. Spring Boot学习

    2024-05-15 13:22:13       30 阅读
  4. fastapi+vue实现导出功能

    2024-05-15 13:22:13       32 阅读
  5. 深入理解Qt的信号与槽机制

    2024-05-15 13:22:13       26 阅读