C++:现代类型转换

C++:现代类型转换

    • C/C++的旧式类型转换
    • C++的现代类型转换
      • 静态类型转换 static_cast
      • 重新解释转换 reinterpret_cast
      • 常量类型转换 const_cast
      • 动态类型转换 dynamic_cast


C/C++的旧式类型转换

在C语言中,类型转换被分为显式和隐式,常见的类型转换如下:

  1. 整型之间,intlong longlongshrotchar等互相转换
  2. 整型与浮点数之间,intlong等与floatdouble之间的转换
  3. 整型与布尔值,非0为true,0为false
  4. 指针与布尔值,空指针为false,非空指针为true
  5. 整型与指针之间
  6. 不同指针之间

C++后,增加了类的概念,类型就丰富了起来,于是又多出了以下与类相关的转换:

  1. 内置类型 → 自定义类型,依赖构造函数
  2. 自定义类型 → 自定义类型,依赖构造函数
  3. 自定义类型 → 内置类型,依赖操作符重载

前两个是类中常见的,不讲解了,而C++支持通过操作符重载来进行自定义类型 → 内置类型的转换

比如:

class A
{
public:operator int(){return _a + _b;}operator double(){return _a / _b;}
private:int _a = 3;int _b = 5;
};

A支持两种类型的转换:A -> intA -> double,这是通过操作符重载实现的,operator type就可以完成到type类型的转换。注意的是,类型转换的返回值是固定的,所以operator type左侧不用写函数返回类型。

那么类A就可以完成以下转换:

A aa;
int x = aa;
double y = aa;

以上所有类型转换,都是旧式的,是基础C语言的显式与隐式的规则,衍生出来的各种类型转换。

C语言风格的类型转换很简洁,但是也有缺点:

  1. 转换的分类过于笼统,只分显式和隐式
  2. 隐式转换非常坑,很容易出bug

比如以下代码:

size_t pos = 0;
int end = 10;while (end >= pos)
{cout << end << endl;end--;
}

这个代码,乍一看没问题,输出10 - 0的所有数字,但是其实这是一个死循环代码。

由于pos的类型是无符号整型,当endpos放在一个表达式中比较,此时end会发生类型转化,从有符号变成无符号,那么end就永远>= 0,陷入死循环了。

这个问题就是隐式类型转换导致的。


C++的现代类型转换

C++提供了四种类型转化,以更加细致安全的方式来区分各种类型转换。

静态类型转换 static_cast

用途:

在类型之间进行安全的转换

格式:

static_cast<type>(expression)

任何能够明确的类型转换都可以使用static_caststatic_cast不能转换掉底层constvolatile),以上说明中,安全的转换不是指编译器帮你确认安全,而是说程序员自己确定了这个转换是安全的之后,再用static_cast

特点在于:不提供运行时的检查,所以叫静态类型转换

主要在以下几种场合中使用:

  • 整型浮点型的转换(原先的隐式转换):
int x = 10;
double y = static_cast<double>(x);

只要是隐式转换可以做到的,都推荐改用static_cast

  • 派生类基类的转换(上行转换):
class Base
{//...
};
class Derived : public Base
{ //...
};int main()
{Derived d;Base* b = static_cast<Base*>(&d);return 0;
}
  • void*具体类型指针的转换(指针之间的转换):
void* p = malloc(sizeof(int));
int* x = static_cast<int*>(p);

重新解释转换 reinterpret_cast

用途:

进行不安全的类型转换

格式:

reinterpret_cast<type>(expression)

这是非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。

仅仅重新解释内存中的二进制表示,而不执行任何实际的类型检查,因此非常危险,不建议使用。

  • 将一个指针转换为整型:
int x = 1;
int p = reinterpret_cast<int>(&x);
  • 在不同的指针类型之间进行转换:
struct Data { int a; double b; };
Data d;
double* p = reinterpret_cast<double*>(&d.b);

常量类型转换 const_cast

用途:

移除或添加对象的const属性和volatile属性

格式:

const_cast<type>(expression)
  • 移除const属性:
const int x = 10;
int* p = const_cast<int*>(&x);
*p = 20; // 修改常量对象的值

但是以上代码暗含一个小问题,我们尝试输出一下xp

cout << x << endl;
cout << *p << endl;

输出结果:

10
20

怎么回事?我们明明通过指针修改了x,为什么x10,而p20

实际上,C++的常量不存储在常量区,而是存储在栈区,而编译器会对const变量优化,把const变量放到寄存器中,后续只要访问变量a,都去寄存器中查找。我们通过指针p修改的变量,其实修改的是栈区中的数据,没有修改寄存器的数据,因此访问a还是从寄存器中读出了10

为了解决这个问题,可以用volatile关键字修饰const变量,此时就不会把常量存在寄存器中了。

volatile const int x = 10;
int* p = const_cast<int*>(&x);
*p = 20;cout << x << endl;
cout << *p << endl;

此时的输出结果为:

20
20

由于volatile的出现,const的种类也变多了,比如以下代码:

volatile const int x = 10;
const int* p = &x;

这是一段错误的代码,&x的类型是volatile const int*,与const int*不同,此时编译器会报错。const_cast不止可以消除const属性,也可以消除volatile属性

  • 消除volatile属性
volatile const int x = 10;
const int* p = const_cast<const int*>(&x);

动态类型转换 dynamic_cast

用途:

在继承层次结构中进行安全的向下转换,检查转换是否成功。

格式:

dynamic_cast<type>(expression)

相比static_castdynamic_cast会在运行时检查类型转换是否合法,具有一定的安全性。由于运行时的检查,所以会额外消耗一些性能。

dynamic_cast常用于基类与派生类之间的转换,分为两种情况:

上行转换:从 派生类指针/引用 向 基类指针/引用 的转换,本质上是赋值兼容,一般来说是安全的
下行转换:从 基类指针/引用 向 派生类指针/引用 的转换,安全性不确定

一般来说,上行转换使用static_cast即可,因为这是一个比较安全的行为。而下行转换用dynamic_cast来检测安全性。

有如下特性:

  1. 使用dynamic_cast要求基类中必须有虚函数
  2. 如果转换成功,dynamic_cast返回转换后的指针,如果转换失败,dynamic_cast返回空指针
  • 向下转换,并检查转换是否成功:
class Base
{virtual void foo() {}
};class Derived : public Base
{/* ... */
};int main()
{Base* b = new Base();Derived* d = new Derived();Base* p1 = static_cast<Base*>(d);//上行转换Derived* p2 = dynamic_cast<Derived*>(p1);Derived* p3 = dynamic_cast<Derived*>(b);cout << p2 << endl;cout << p3 << endl;return 0;
}

以上代码中,完成了两次下行转换,分别是p1 -> p2d -> p3的转换。而p1是通过b转化而来的,所以p1是指向派生类的基类指针,而d是指向基类的基类指针。

因此p1转回派生类指针,是合理的,而d转为派生类指针,是危险的,于是dynamic_cast会阻止d转为派生类指针。

输出结果:

0000015220F64100
0000000000000000

可见p3得到了空指针,转化失败,而p2得到正常转化后的指针。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/5837.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动应用开发:Android vs iOS平台的选择与挑战

在移动应用开发领域&#xff0c;选择开发平台是一个关键的决策&#xff0c;通常涉及考虑Android和iOS这两大主流平台。每个平台都有其特定的优势和挑战&#xff0c;开发者需要根据目标市场、预算、功能需求和技术能力来做出选择。 ### Android和iOS的主要差异 1. **市场份额*…

C语言-预处理

一、C语言编译过程 预处理、编译、汇编、链接 1&#xff1a;预编译 将.c 中的头文件展开、宏展开 生成的文件是.i文件 2&#xff1a;编译 将预处理之后的.i 文件生成 .s 汇编文件 3、汇编 将.s汇编文件生成.o 目标文件 4、链接 将.o 文件链接成目标文…

es优化实践(es升级带来的大坑)->问题与痛点->优化过程->原理分析->最佳实践

这里分享一个ES2.X升级到ES5.X带来的天坑问题 结论先行 先上结论(最佳实践) es5以后版本 对于某个字段 1.字段用于terms查询,则字段定义为keyword类型, 如果定义为数值类型(number,long,short等)会有严重的性能问题 ,查询耗时会很长2.字段用于range查询,则字段定义为数值类型…

网络协议安全:SYN Flood原理和防御措施,使用hping3实现SYN Flood

「作者简介」:2022年北京冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖系统安全、信息收集等12个知识域的一百多个知识点,持续更新。 这一章节我们需要知道S…

【Docker】docker部署lnmp和搭建wordpress网站

环境准备 docker&#xff1a;192.168.67.30 虚拟机&#xff1a;4核4G systemctl stop firewalld systemctl disable firewalld setenforce 0 安装docker #安装依赖包 yum -y install yum-utils device-mapper-persistent-data lvm2 #设置阿里云镜像 yum-config-manager --add…

google chromeDriver 地址

#chrome driver 下载地址 https://getwebdriver.com/chromedriver #chrome brower下载地址 https://googlechromelabs.github.io/chrome-for-testing/ centos 安装的dockerFile的部分命令&#xff1a; #安装browser wget https://dl.google.com/linux/chrome/rpm/stable/x86_…

【Web】D^3CTF之浅聊d3pythonhttp——TE-CL请求走私

目录 step0 题目信息 step1 jwt空密钥伪造 step1.5 有关TE&CL的lab step2 TE-CL请求走私 payload1 payload2 step0 题目信息 注意到题目源码前端是flask写的&#xff0c;后端是web.py写的 frontend from flask import Flask, request, redirect, render_templat…

Mac no library called “libcairo-2“ was found

最近在运行ernibe-bot researcher的时候出现了下面的问题&#xff1a; OSError: no library called "cairo-2" was found no library called "cairo" was found no library called "libcairo-2" was found cannot load library libcairo.so.2: …

Java中的访问修饰符有哪些?各有什么作用?

Java中有四种访问修饰符&#xff08;Access Modifiers&#xff09;&#xff0c;它们定义了类、方法、变量和构造函数的访问控制级别。这些修饰符从最宽松的访问级别到最严格的访问级别依次为&#xff1a;public, protected, default&#xff08;没有修饰符时使用的默认访问级别…

C++ 类对象初始化

一、默认初始化 当创建一个类的实例时&#xff0c;如果没有定义构造函数&#xff0c;系统会自动生成一个无参构造函数&#xff0c;并自动初始化成员变量。内置类型的变量&#xff08;如int、double、指针等&#xff09;会被初始化为一个未定义的值&#xff0c;而自定义类型的变…

基于Python的人脸识别系统设计与实现(论文+源码)_kaic

基于Python的人脸识别系统设计与实现 摘 要 随着人工智能的发展,人脸识别系统在我们的生活中越来越被广泛应用。人脸识别系统是指能够从数字图像或视频源中识别人的技术。人脸识别系统可以通过多种方法工作&#xff0c;但是&#xff0c;它们通常是通过将给定图像中的面部特征与…

论文笔记总结

写论文不能只讲概念&#xff0c;一定要结合项目理论实际。》例如某xxx具体的项目例子&#xff0c;不能描述某一个软件的功能。 1.历年真题 2.十段式划分&#xff08;回应子题目&#xff0c;三个子题目&#xff09; 3.论文模板&#xff0c;万能模板 4.具体主题相关 第一个主…

202012青少年软件编程(Python)等级考试试卷(一级)

第 1 题 【单选题】 运行下方代码段&#xff0c;输出是6&#xff0c;则输入的可能是&#xff08; &#xff09;。 a eval(input())print(a)A :8%2 B :8/2 C :3*2 D :3**2 正确答案:C 试题解析: 第 2 题 【单选题】 关于Python变量&#xff0c;下列叙述正确的是&#x…

Rust基本特性补充

虚幻数据PhantomData 实际上&#xff0c;结构体本身也是可以有生命周期的&#xff0c;例如: struct Tmp<a>{index: &a u32 } 上述声明中&#xff0c;虽然index为一个引用&#xff0c;但是这样声明后&#xff0c;相当于告诉编译器&#xff0c;Tmp对象的生命周期会和…

Spring Cloud——LoadBalancer

Spring Cloud——LoadBalancer 一、负载均衡&#xff08;LoadBalance&#xff09;1.LoadBalancer本地负载均衡客户端 VS Nginx服务端负载均衡区别 二、LoadBalancer1.Spring RestTemplate as a LoadBalancer Client2.编码使用DiscoveryClient动态获取所有上线的服务列表3.从默认…

《自动机理论、语言和计算导论》阅读笔记:p215-p351

《自动机理论、语言和计算导论》学习第 11 天&#xff0c;p215-p351总结&#xff0c;总计 37 页。 一、技术总结 1.constrained problem 2.Fermat’s lats theorem Fermat’s Last Theorem states that no three positive integers a, b and c satisfy the equation a^n b…

SNMP自动遍历所有节点并GET

echo on setlocal EnableDelayedExpansion SnmpWalk.exe -r:10.253.2.38 -v:2 -t:5 -c:“public_default” > 1.txt REM snmp mib节点遍历前记录设备cpu和内存利用率 SnmpGet.exe -q -r:10.253.2.38 -v:2 -t:5 -c:“public_default” -o:1.3.6.1.4.1.31648.3.15.10.0 >&…

unity中 UnityWebRequest.Post和 UnityWebRequest uwr = new UnityWebRequest两种方法有什么区别

在Unity中&#xff0c;UnityWebRequest.Post 和 UnityWebRequest uwr new UnityWebRequest(...) 是两种不同的方式来创建和发送HTTP POST请求&#xff0c;但它们之间有一些关键的区别和用法上的差异。 1. UnityWebRequest.Post (静态方法) UnityWebRequest.Post 是一个静态方…

PostgreSQL数据类型总结

PostgreSQL数据库相比其他数据库&#xff0c;支持更多的数据类型&#xff0c;包括常用的数值类型、字符串类型、日期/时间类型外&#xff0c;还有几何类型、网络地址类型、xml类型和json类型&#xff0c;且还可以使用CREATE TYPE自行添加数据类型&#xff0c;本文主要介绍Postg…

过渡与动画

单元素/组件过渡 Vue在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的过渡效果&#xff08;一个淡入淡出的效果&#xff09; 在条件渲染&#xff08;使用v-if&#xff09;、条件展示&#xff08;使用v-show&#xff09;、动态组件、组件根节点等情形中&#xff0c;可…