C++中的类型转换操作符:static_cast reinterpret_cast const_cast dynamic_cast

news/2024/7/7 20:39:19 标签: c++, 开发语言

目录​​​​​​​

C语言中的类型转换

C++中的类型转换

C++中的类型转换操作符

static_cast

reinterpret_cast

const_cast

volatile关键字

赋值兼容

dynamic_cast


C语言中的类型转换

基本概念:赋值运算符左右两侧类型不同,或形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,需要发生类项转换,C语言中有两种形式的类型转换,隐式类型转换和显示类型转换

注意事项:有关联的类型才可以相互转换,小类型会转换为大类型

  1. 整型之间的隐转(int、long、short)
  2. 整型和浮点数间的隐转
  3. 整型和字符型间的隐转
  4. bool和整型、指针间的隐转(整数0和空指针均表示假)
  5. 整型和指针间的强转(地址的本质是一个编号所以可以)
//整型转换为指针
//缺点:在绝大多数情况下是不安全的,除非你确切知道这个整数表示的内存地址是有效的
int number = 1234;
int *ptr = (int *)number;

//指针转换为整型:通常用于获取指针的内存地址
//缺点:如果在64位系统上,将指针转换为32位整数(如 int),可能会导致数据丢失。因此,建议使用 intptr_t 或 uintptr_t 类型,它们在 <stdint.h> 头文件中定义,可以确保适当的大小来存储指针值
int *ptr = &number;
int address = (int)ptr;

关于intptr_t和uintptr_t可以查看:intptr_t、uintptr_t数据类型的解析

  1. 不同类型的指针间的强转
//不同类型的指针间的强制类型转换
char s = 'a';
char* ptr1 = &s;
int* ptr2 = (int*) ptr1;

显示类型转换的缺点:

  • 可能导致数据丢失当从一个较大的数据类型转换为一个较小的数据类型时,可能会丢失数据。例如,将 double 转换为 int 时,小数部分会被截断
double d = 3.14; int i = (int)d; // i = 3, 小数部分丢失
  • 降低代码可读性频繁使用显式类型转换会使代码变得难以阅读和理解,尤其是当类型转换逻辑复杂时
float f = (float)((int)doubleValue * 2.0);
  • 增加错误风险进行类型转换时,容易忽略一些潜在的错误,如溢出或数据精度问题,导致难以发现的bug
long longValue = 1234567890123456789LL; 
int intValue = (int)longValue; // 可能会溢出

隐式类型转换的缺点:

  • 隐蔽的数据丢失int 转换为 float 时,如果整数的值超过了浮点数的精度范围,可能会丢失精度

int i = 123456789; float f = i; // 可能会丢失精度
  • 难以调试和维护隐式类型转换发生在幕后,可能导致意外的结果和难以调试的问题。出错时需要仔细检查代码,以确保所有隐式转换都符合预期

//int被转换为size_t,当传入的pos = 0时,会陷入死循环 
void Insert(size_t pos)
{
	int end = 10;
	// 比较的时候end会隐式类型转换成size_t,再比较
	while (end >= pos)
	{
		cout << end << "挪走" << endl;
		--end;
	}
}

C++中的类型转换

基本概念:C++中通过构造函数和重载可以实现内置类型与自定义类型间、自定义类型间的转换

  • 内置类型->自定义类型:借助构造函数,隐式类型转换
//单参数的构造函数,支持内置类型隐式转换成自定义类型
string s1 = "111111";
const string& s2 = "11111";
  • 自定义类型->内置类型:重载某个类型(operator +类型名)
class A
{
public:
	operator int()
	{
		return _a1 + _a2;
	}
private:
	int _a1 = 1;
	int _a2 = 2;
};

int main()
{
	A aa;
	int a = aa;
	int b = (int)aa;
	cout << a << endl;
	cout << b << endl;
	return 0;
}

  • 自定义类型->自定义类型:借助构造函数进行隐式类型转换(initializer_list和容器间)

C++中的类型转换操作符

基本概念:C++依然兼容C语言的两种类型转换方式,但是为了加强类型转换时的可视性,C++引入了四种类型转换操作符,它们是一些进行类型转换时的命名建议,并不强制

static_cast

基本概念:进行有关联的类型间的隐式类型转换(编译时进行,也叫静态转换)

int main()
{
	//C++的隐式类型转换:明显
	double d = 12.34;
	int a1 = static_cast<int>(d);
	cout << a1 << endl;//12
	
	//C语言的隐式类型转换:不明显
	int a2 = d;
	cout << a2 << endl;//12
	return 0;
}

reinterpret_cast

音标:riːɪnˈtɜːrprət

基本概念:进行强制类型转换,不进行类型检查

int main()
{
	int a = 12;
	// int* p = static_cast<int*>(a); 报错,int和int*不是两个相关的类型
	int* p = reinterpret_cast<int*>(a);
	cout << p << endl;
	return 0;
}

const_cast

基本概念:删除对象的const属性

int main()
{
	const int a = 10;
	int* p = const_cast<int*>(&a); // 去除const属性
	*p = 20; // 尝试修改值
	cout << a << endl;//a == 10
	cout << *p << endl;//*p == 20
	return 0;
}

        对比监视列表和控制台,可以发现虽然在内存中a的值变为了20,但是打印时a仍为原来的10,这是因为编译器知道const修饰的变量不会被修改,那么编译器在编译阶段就会对其优化,即编译器会将常量的值直接内联到使用该常量的地方,而不是每次都访问内存中的存储位置,这一优化技术称为“常量折叠”或“常量传播

补充:const修饰的局部变量仍然存放在栈上,修饰的全局变量才放在常量区中

volatile关键字

基本概念:用于指示编译器不对变量进行优化(带入上述代码再试一试即可)

volatile const int a = 10;//表示不会对a进行优化

结论:const_cast和reinterpret_cast都是强制类型转换(const_cast的第一个例子中的a去掉const修饰也能const_cast也能正常使用,且a打印时也为20),但是单独将const_cast拿出来就是因为可能出现因为编译器对const修饰的变量进行优化而导致的内存可见性

赋值兼容

基本概念:有继承关系的派生类与基类之间所遵循的赋值规则,包括向上和向下转型

  • 向上转型:派生类对象赋值给基类对象(发生切片,切片  != 类型转换)、派生类的指针/引用赋值给基类的指针/引用(通常情况下都支持)(即这两种情况正常情况下都是可以支持的
#include <iostream>
using namespace std;

class A
{};

class B : public A
{};

int main()
{
	//向上转型
	B bb;
	A aa = bb;//派生类对象直接赋值给基类对象,发生切片
	A* ptr = &bb;//派生类对象的指针可以直接赋值给基类对象的指针
	B& cc = bb;
	A& str = cc;//派生类对象的引用可以直接赋值给基类对象的引用
	return 0;
}
  • 向下转型:基类对象赋值给派生类对象(除非有相关的构造函数、否则即使是强转也转不了)、基类对象的指针/引用赋值给派生类对象的指针/引用(可以通过强转实现,但有越界风险需要得到控制)

//有相应的构造函数才能实现基类对象到派生类对象间的转换
#include <iostream>
using namespace std;

class A {
public:
    int baseData;
    A() : baseData(0) {}
    A(int data) : baseData(data) {}
};

class B : public A {
public:
    B() {};

    // 自定义构造函数,实现基类到派生类的转换
    B(const A& a) : A(a) {
        cout << "hello" << endl;
     }

    // 自定义赋值运算符,实现基类到派生类的转换
    B& operator=(const A& a) {
        cout << "world" << endl;
        return *this;
    }
};

int main() {
    A aaa(10);
    B bbb;

    bbb = aaa;  // 使用自定义赋值运算符
    B bbb2 = aaa;  // 使用自定义构造函数
    return 0;
}

dynamic_cast

基本概念:为了能控制向下转型时的越界问题、C++提供了dynamic_cast类型转换操作符,dynamic_cast会在转换前检查是否能转换成功,可以转换就转换、不能转换就返回0

#include <iostream>
using namespace std;

class A
{
public:
	virtual void f() {}

	int _a = 0;
};

class B : public A
{
public:
	int _b = 1;
};

void fun(A* pa)
{
	// 向下转换:父->子
	// pa指向子类对象,转回子类,是安全的
	// pa指向父类对象,转回子类,是不安全的,存在越界的风险问题

	// 不安全
	//B* pb = (B*)pa;

	//  pa指向子类对象,转回子类,正常转换
	//  pa指向父类对象,转回子类,转换失败
	B* pb = dynamic_cast<B*>(pa);
	if (pb)
	{
		cout << pb << endl;
		cout << pb->_a << endl;
		cout << pb->_b << endl;
	}
	else
	{
		cout << "转换失败" << endl;
	}
}

int main()
{
	A a;
	B b;

	fun(&a);//父类转为子类
	fun(&b);//子类转为子类
	return 0;
}

注意事项:dynamic_cast在使用时,基类要有虚函数(有就行)

~over~


http://www.niftyadmin.cn/n/5535299.html

相关文章

FastAPI-Cookie

fastapi-learning-notes/codes/ch01/main.py at master Relph1119/fastapi-learning-notes GitHub 1、Cookie的作用 Cookie可以充当用户认证的令牌&#xff0c;使得用户在首次登录后无需每次手动输入用户名和密码&#xff0c;即可访问受限资源&#xff0c;直到Cookie过期或…

设计模式-结构型-08-组合模式

文章目录 1、学校院系展示需求2、组合模式基本介绍3、组合模式示例3.1、 解决学校院系展示&#xff08;透明模式1&#xff09;3.2、高考的科目&#xff08;透明模式2&#xff09;3.3、高考的科目&#xff08;安全组合模式&#xff09; 4、JDK 源码分析5、注意事项和细节 1、学校…

BUG:AttributeError: module ‘websocket‘ has no attribute ‘enableTrace’

AttributeError: module ‘websocket’ has no attribute enableTrace’ 环境 windows 11 Python 3.10websocket 0.2.1 websocket-client 1.8.0 websockets 11.0.3 rel 0.4.9.19详情 一开始…

PromptCraft-Robotics部署步骤和问题记录

GitHub - microsoft/PromptCraft-Robotics: Community for applying LLMs to robotics and a robot simulator with ChatGPT integration 部署环境&#xff1a;UE4.27 Visual Studio 2022 Arisim1.8.1 可参考&#xff1a;git clone https://github.com/Microsoft/AirSim.gi…

保函到期提醒是银行或金融机构提供的一项服务,旨在确保客户及时了解保函即将到期的情况,从而避免因保函过期而导致的风险或违约责任。

保函到期提醒是银行或金融机构提供的一项服务&#xff0c;旨在确保客户及时了解保函即将到期的情况&#xff0c;从而避免因保函过期而导致的风险或违约责任。以下是保函到期提醒的一些关键方面&#xff1a; 1. **保函定义**&#xff1a; - 保函是一种由银行出具的书面承诺&…

CPU通过网络将IP camera的RTSP流(H.264编码或是H.265编码)拉回, 交给GPU解码并显示的处理流程

这个流程涉及到从IP摄像头获取视频流&#xff08;通过RTSP协议&#xff09;&#xff0c;然后将流传输给GPU进行解码和显示的过程。详细的流程描述如下&#xff1a; 1. 获取视频流: - **IP摄像头**: 摄像头通过RTSP&#xff08;Real-Time Streaming Protocol&#xff09;将…

数据结构——树的基础概念

目录 1.树的概念 2.树的相关概念 3.树的表示 &#xff08;1&#xff09;直接表示法 &#xff08;2&#xff09;双亲表示法 (3)左孩子右兄弟表示法 4.树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 1.树的概念 树是一种非线性的数据结构&#xff0…

华为 RIP 协议中 RIP 兼容版本、RIPv1、RIPv2 在收发 RIP 报文时的区别

华为 RIP 协议中 RIP 兼容版本、RIPv1、RIPv2 的区别 为了更好地支持实际环境中路由器对 RIP 的支持&#xff0c;华为 VRP 平台具有一个兼容版本&#xff0c;默认情况下启动 RIP 进程后&#xff0c;如果没有配置 RIP 版本&#xff0c;该版本就为兼容版本&#xff0c;对 versio…