当前位置: 首页 > news >正文

【C++】-- 类型转换

目录

前言

C语言中的类型转换

C++强制类型转换

static_cast(static静止的)

reinterpret_cast(reinterpret重新解释)

const_cast(const常量)

总结

dynamic_cast(dynamic动态)

RTTI

常见面试题


前言

        C++继承了不少C语言的知识,因为C++最开始就是从C语言出来的,于是也就导致了C++将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语言那就当然是针对于内置类型啦)

C语言是意义相近的类型允许隐式类型的准换,如:整形家族和浮点数家族。

  • 整形之间转换是:小的可以转大的提升,大的也可以转小的截断
  • 整形和浮点之间转换是:通过补位的方式,整形转浮点补浮点就行因为精度,浮点转整形丢掉精度

        可以说:它们都是用来表示数据的大小,只是空间的大小不一样,表示的范围不一样。还有就是浮点数的存储机制也不一样,其还能表示小数点后的精度。(本质:意义相近)

C语言中对于显示强制类型转换是意义不相近的类型,值转换后有意义

Note:

        整体来说,隐式类型还好,但是显示类型是有巨大的坑的

在日常中最简单的insert函数实现(数据移动)中,就有问题。

void Insert(size_t pos, char ch)
{size_t _size = 5;// ……int end = _size - 1;while(end >= pos){_str[end + 1] = _str[_end];--end;}
}Insert(3, 'A'); // 没有问题
Insert(0, 'A'); // 有问题

        在其中,第二个Insert就存在问题,因为在判断的时候就会发生隐式类型的转换。

         在这个判断的时候应该就是end为-1,pos为0然后,退出while循环,但是此处它会悄悄地进入执行,因为在一个操作符的两端的操作数也会发生隐式类型的转换。这个时候会悄悄的产生一个变量将end进行了提升(从无符号提升成有符号),然后就会导致数据的越界。

        所以,C语言留下的这个东西也是一个坑。并且没有办法因为为了兼容前面的内容,所以没有办法移除隐式转换,所以我们需要注意隐式转换的坑。

        于是,便有了C++的规范化。

#问:为什么C++需要四种类型转换?

C风格的转换格式很简单,但是有不少缺点的:
  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失。
  2. 显式类型转换将所有情况混合在一起,代码不够清晰。
        因此C++提出了自己的类型转化风格。
Note:
        因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。

C++强制类型转换

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

  • static_cast
  • reinterpret_cast
  • const_cast
  • dynamic_cast

        这个时候C语言的隐式类型转换的一套还是可以使用的,只不过一般编译器会报警告,不影响运送,但是会告诉你,如:浮点数转换为整形精度可能会丢失。

static_cast(static静止的

        static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换 - 意义相近的类型

int main()
{double d = 12.34;int a = static_cast<int>(d);std::cout << a << std::endl;return 0;
}

        不是相近的类型不可以运用其来转换。

int main()
{int* p = &a;// 不支持的int address = static_cast<int>(p);return 0;
}

        其是会报错为,如:"static_cast":无法从"int*"转换为"int",的错误(无效的)

reinterpret_cast(reinterpret重新解释

        reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型 - 意义不相关的类型

int main()
{double d = 12.34;int a = static_cast<int>(d);int* p = &a;// 不支持的//int address = static_cast<int>(p);int address = reinterpret_cast<int>(p);return 0;
}

         对于const修饰的变量,不能转化,因为C++的规范会进行检查。

int main()
{const int a = 2;// 不支持int* p = reinterpret_cast<int*>(&a)*p = 3;return 0;
}

补充:

        甚至有一些地方还可以支持一些比较bug的转化。因为其相当于重新解释了,转换成完全另外一个类型。如:其实是一个指针,转换成为了一个数据大小,或者是一个数据大小转换成为了指针。

const_cast(const常量

        const_cast最常用的用途就是删除变量的const属性,方便赋值。

int main()
{const int a = 2;int* p = const_cast<int*>(&a)*p = 3;return 0;
}

        一个奇葩的操作。

int main()
{const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << endl;cout << *p << endl;return 0;
}

#问:上面的奇葩的操作的输出结果是什么?

简单的来看是不是以为是:3、3?那就错了。

        p是a的地址,那*p就是a,这么想上面没错(const对象虽然不能改,但是在C++里面const修饰的变量叫做常变量,是不能被直接的修改,但是可以被间接接的修改)

修改const变量:

        就像上述,就是改const的方法。这种方式很bug,我们取到const修饰的变量的地址,然后通过地址间接的强制去改。本质的原因就是C++的const修饰的变量,并没有存储到常量区当中去,const修饰的变量与其他的变量一样,都存在栈上的,所以使用指针的方式是可以被修改的。

其实运行结果是:2、3

        而且我回很奇特的发现,我们对于数据进行监视数据是:3、3,然而实际打印出的数据却是2,3

        这个的原因是由来于编译器的优化,编译器对于const类型的变量是有优化的。比如说有一些编译器的优化是会将数据放在寄存器的,因为对于编译器来说,这个const修饰的对象,是只会读而并不会进行修改的,于是为了提高运行的效率,于是直接将数存储到了寄存器当中,你要就取。所以虽然内存中的数据被进行了更改,但是在寄存器看来就是没变。

        有一些编译器的处理方式,不是放到寄存器,而是直接搞死,如同一个宏。不取直接就给初始化时的值。

        所以也就是为什么 const_cast 也可以作为毫不相关的转换(重新解释的转换)。 const_cast 单独形成一类,也就是为了告诉我们这个地方很危险。还有一个方法可以解决这个问题:volatile(关键字),就是为了告诉编译器这个地方不要进行优化,直接去内存中取这个值。

总结

  1. 兼容C隐式类型转换和强制类型转换。
  2. 期望我们需要使用C语言的,期望我们用规范的C++显示的强制类型转换。
  3. static_cast(隐式类型转换)、reinterpret_cast、const_cast(强制类型转换)
        C++的一套更规范一些,使用它的一看就知道是相近类型还是没有关联类型的准换,看见static_cast就知道很危险要小心。

dynamic_cast(dynamic动态

        是C语言没有的,是C++自己增加的,适用于向下转化。dynamic_cast用于将一个父类对象的指针 / 引用转换为子类对象的指针或引用(动态转换)。

  • 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则) ——  天然支持

        严格的来说:向上转型是不属于隐式类型转换,也不属于显示类型转换。它可以说是C++的一个特例,其不需要进行转换,它是赋值兼容的。

  • 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:
  • dynamic_cast只能用于父类含有虚函数的类 —— 否者编译直接报错。
  • dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。 
        我们日常中所用的继承关系的转换就是天然支持向上转型的,只有在继承的向上转里面是支持的。
class A
{
public :virtual void f(){}
};class B : public A
{};int main ()
{B bb;A aa = bb;A& ra = bb;return 0;
}

        dynamic_cast是适用于向下转型,父转子(用dynamic_cast转型是安全的)。因为按道理父类对象是无论如何都不能转子类的,就是使用dynamic_cast也是不行的。

class A
{
public :virtual void f(){}
};class B : public A
{};int main ()
{A aa;// 父类对象无论如何都是不允许转换成子类对象的// B bb = dynamic_cast<B>(aa);// B bb = (B)aa;return 0;
}

        对象是怎么样都不允许转的,但是指针与引用是要允许转的。因为涉及到一个指针,是有可能指向父类,也有可能指向子类。而如果是父类指针指向是父类的对象,那么就会导致出现越界问题。而如果是父类指针指向是子类的对象,那就没有问题。于是C++提供了一个dynamic_cast,让我们的行为是安全的,即:

  1. 如果父类指针指向的是子类的对象,那么可以转换,转换表达式返回正确的地址。
  2. 如果父类指针指向的是父类的对象,那么不能转换,转换表达式返回nullptr。
class A
{
public :virtual void f(){}
};class B : public A
{};int main ()
{// 如果pa是指向子类,那么可以转换,转换表达式返回正确的地址// 如果pa是指向父类,那么不能转换,转换表达式返回nullptrB bb;A* pa = bb;B* pb = dynamic_cast<B*>(pa); // 安全的//B* pb = (B*)pa;             // 不安全if (pb){cout << "转换成功" << endl;pb->_a++;pb->_b++;cout << pb->_a << ":" << pb->_b << endl;}else{cout << "转换失败" << endl;pa->_a++;cout << pa->_a << endl;}
}

总结:

        其中,向上转型就是所说的切割/切片,是语法天然支持的,不需要进行转换,而向下转型是语法不支持的,需要进行强制类型转换。

#include <iostream>class A1
{
public:virtual void f(){}
public:int _a1 = 0;
};class A2
{
public:virtual void f(){}
public:int _a2 = 0;
};class B : public A1, public A2
{
public:int _b = 1;
};int main()
{B bb;A1* ptr1 = &bb;A2* ptr2 = &bb;std::cout << ptr1 << std::endl;std::cout << ptr2 << std::endl << std::endl;B* pb1 = (B*)ptr1;B* pb2 = (B*)ptr2; // C语言的强制类型转换是回得到开头的std::cout << pb1 << std::endl;std::cout << pb2 << std::endl << std::endl;B* pb3 = dynamic_cast<B*>(ptr1);B* pb4 = dynamic_cast<B*>(ptr2);std::cout << pb3 << std::endl;std::cout << pb4 << std::endl << std::endl;return 0;
}

Note:
        强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换。

RTTI

        RTTI(Run-time Type identifification的简称),即:运行时类型识别。
C++通过以下方式来支持RTTI:
  1. typeid运算符 —— 拿到变量的类型的字符串。
  2. dynamic_cast运算符 —— 父类的指针指向父类对象,还是子类对象,只能用于父类含有虚函数的类(多态)
  3. decltype —— 推导一个对象的类型,这个类型可以用来定义另一个对象。

常见面试题

#问:C++中的4种类型转换分别是:____ 、____ 、____ 、____。

        static_cast、reinterpret_cast、const_cast和dynamic_cast。

#问:说说4种类型转换的应用场景。

  • static_cast用于相近类型的类型之间的转换,编译器隐式执行的任何类型转换都可用static_cast。
  • reinterpret_cast用于两个不相关类型之间的转换。
  • const_cast用于删除变量的const属性,方便赋值。
  • dynamic_cast用于安全的将父类的指针(或引用)转换成子类的指针(或引用)。

相关文章:

【C++】-- 类型转换

目录 前言 C语言中的类型转换 C强制类型转换 static_cast&#xff08;static静止的&#xff09; reinterpret_cast&#xff08;reinterpret重新解释&#xff09; const_cast&#xff08;const常量&#xff09; 总结 dynamic_cast&#xff08;dynamic动态&#xff09; …...

汇编基础语法和指令总结+案例(用32位汇编实现插入排序)

目录 前提知识 案例 c的插入排序 32位汇编代码 代码分析 效果展示 前提知识 常用指令add指令 sub指令 mul乘法指令 div除法指令 inc&#xff08;自增&#xff09;&#xff08;即&#xff09; dec&#xff08;自减&#xff09;&#xff08;即--&#xff09; cmp&#xf…...

C++多线程--线程安全的单例模式

0 引言 由于最近事情比较多,所以很久没有更新相应的专栏了。目前事情基本告一段落,重新恢复相应专栏的更新。 本文主要讲解在C++并发编程中如何实现线程安全的单例模式。本文主要由如下几部分构成 臭名昭著的double-check单例实现四种线程安全的单例模式单例模式使用中所带…...

(Android-RTC-9)PeerConnectionFactory

开篇前瞎扯。很久没发技术文章了&#xff0c;此文一直放着草稿箱没有完成&#xff0c;感觉自己在家庭和工作中找到了拖延的借口&#xff0c;开始慢慢变得懒惰了&#xff0c;那是万万不行的。恰逢2023开年ChatGPT的爆火&#xff0c;更让我这些普通程序员危机感瞬间飙升&#xff…...

Vector - CAPL - 定时器函数和使用

定时器在C语言中的使用我想学习过C编程的都不会陌生&#xff0c;它能够提供延时&#xff0c;完成等待一定的时间&#xff1b;它也可以实现多线程的操作&#xff0c;并行实行某些软件功能。那在CAPL中&#xff0c;定时器又能做哪些工作呢&#xff1f;又是怎么使用的呢&#xff1…...

【嵌入式C】常见问题

1、goto的使用场景有哪些&#xff1f;并讨论其局限&#xff1f; &#xff08;1&#xff09;常用来跳出死循坏&#xff1b; &#xff08;2&#xff09;在linux开发中&#xff0c;常用于打印错误&#xff1b; &#xff08;3&#xff09;goto在某些使用场合会破坏程序的栈逻辑&…...

[神经网络]Transfomer架构

一、概述 Transfomer架构与传统CNN和RNN最大的区别在于其仅依赖自注意力机制&#xff0c;而没有卷积/循环操作。其相较于RNN&#xff0c;不需要进行时序运算&#xff0c;可以更好的进行并行&#xff1b;相较于CNN&#xff0c;其一次可以关注全图而不局限于感受野尺寸。 二、模…...

C++之多态 虚函数表

多态 多态是在不同继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的行为。 需要区分一下&#xff1a;1、菱形虚拟继承&#xff0c;是在继承方式前面加上virtual&#xff1b; class Person {}; class Student : virtual public Person {}; class Teacher…...

AI_Papers周刊:第四期

2023.02.28—2023.03.05 Top Papers Subjects: cs.CL 1.Language Is Not All You Need: Aligning Perception with Language Models 标题&#xff1a;KOSMOS-1&#xff1a;语言不是你所需要的全部&#xff1a;将感知与语言模型相结合 作者&#xff1a;Shaohan Huang, Li …...

A Simple Framework for Contrastive Learning of Visual Representations阅读笔记

论文地址&#xff1a;https://arxiv.org/pdf/2002.05709.pdf 目前流行的无监督学范式。通过训练&#xff0c;使模型拥有比较的能力。即&#xff0c;模型能够区别两个数据&#xff08;instance&#xff09;是否是相同的。这在 深度聚类 领域受到广泛的关注。&#xff08;在有监…...

mac安装开发工具:clipy、iterm2、go、brew、mysql、redis、wget等

wget brew install wget clipy Releases Clipy/Clipy GitHub 环境变量 ~下有三个文件 .zshrc .zprofile .bash_profile > cat .zshrc export PATH$PATH:/usr/local/mysql/bin> cat .zprofile eval "$(/opt/homebrew/bin/brew shellenv)"> cat .bas…...

DJ1-1 计算机网络和因特网

目录 一、计算机网络 二、Interent 1. Internet 的介绍 2. Internet 的具体构成 3. Internet 提供的服务 4. Internet 的通信控制 一、计算机网络 定义&#xff1a;是指两台以上具有独立操作系统的计算机通过某些介质连接成的相互共享软硬件资源的集合体。 计算机网络向…...

[1.3.3]计算机系统概述——系统调用

文章目录第一章 计算机系统概述系统调用&#xff08;一&#xff09;什么是系统调用&#xff0c;有何作用&#xff08;二&#xff09;系统调用与库函数的区别&#xff08;三&#xff09;小例子&#xff1a;为什么系统调用是必须的&#xff08;四&#xff09;什么功能要用到系统调…...

【Java开发】JUC进阶 03:读写锁、阻塞队列、同步队列

1 读写锁&#xff08;ReadWriteLock&#xff09;&#x1f4cc; 要点实现类&#xff1a;ReentrantReadWirteLock通过读写锁实现更细粒度的控制&#xff0c;当然通过Synchronized和Lock锁也能达到目的&#xff0c;不过他们会在写入和读取操作都给加锁&#xff0c;影响性能&#x…...

Fragment中获取Activity的一点点建议

平时的Android开发中&#xff0c;我们经常要在Fragment中去获取当前的Activity实例&#xff0c;刚开始的时候可能使用使用Fragment提供的getActivity方法来获取&#xff0c;但是这个方法可能返回null&#xff0c;为了让程序可以正常运行&#xff0c;项目中就出现大量下面这样的…...

Java Math类

Java Math 类是 Java 标准库中提供的一个数学计算类&#xff0c;它提供了很多数学函数&#xff0c;如三角函数、指数函数、对数函数等。在实际工作中&#xff0c;Java Math 类常常被用于处理数学计算问题&#xff0c;例如计算复杂的数学公式、实现数学算法等。本文将详细介绍 J…...

Javascript -- 加载时间线 正则表达式

js加载时间线 1、创建Document对象&#xff0c;开始解析web页面&#xff0c;解析html元素和他们的文本内容后添加Element对象和Text节点到文档中。这个阶段的document.readyState ‘loading’ 2、遇到link外部css&#xff0c;创建线程加载&#xff0c;并继续解析文档 3、遇到…...

gdb/git的基本使用

热爱编程的你&#xff0c;一定经常徘徊在写bug和改bug之间&#xff0c;调试器也一定是你随影而行的伙伴&#xff0c;离开了它你应该会寝食难安吧&#xff01; 目录 gdb的使用 断点操作 运行调试 观察数据 Git的使用 仓库的创建和拉取 .gitignore “三板斧” 常用指令 gd…...

信息安全与数学基础-笔记-④二次同余方程

知识目录二次同余方程的解欧拉判别式Legendre (勒让德符号)二次同余方程的解 什么是二次同余方程的解 注意这里二次同余方程和一次同余方程是不一样的 在x2x^2x2 三 a (mod m) 方程中举例 ↓ 解即剩余类&#xff0c;因为是模m&#xff0c;所以我们在 [ 0, m-1 ]中逐个代入看是…...

Luogu P4447 [AHOI2018初中组]分组

题目链接&#xff1a;传送门 将nnn个可重复的整数分为mmm组&#xff0c;每组中的数必须连续且不重复&#xff0c;使人数最少的组人数最多。 两个最值肯定第一想到二分&#xff0c;每次二分出一个值&#xff0c;判断在这个值为答案的前提下能否完成分组。 在思考判别函数时发现…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...