【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)
小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
c++系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!

目录


前言

【c++】特殊类的设计(不能拷贝的类,只能在堆/栈上创建对象的类,不能被继承的类,单例模式——饿汉模式、懒汉模式)——书接上文 详情请点击<——
本文由小编为大家介绍——【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

一、c语言中的类型转换

在c语言中,如果赋值运算符左右两侧类型不匹配,形参和实参的类型不匹配,返回值类型和接收返回值的类型不匹配就会发生类型转换
c语言中共有两种类型转换:隐式类型转换和显示的强制类型转换

隐式类型转换

  1. 隐式类型转换:隐式类型转换会产生临时变量,编译器在编译阶段进行,如果转换失败,则编译失败,可以隐式转换极为相近的类型,比如浮点数和整形进行转换,对于单参数的构造函数也支持隐式类型转换,c++11中也支持了多参数的隐式类型转换
#include<iostream>usingnamespace std;classA{public:A(int a):_a(a){}private:int _a;};classB{public:B(int b,int c):_b(b),_c(c){}private:int _b;int _c;};intmain(){//隐式类型转换int a =0;double b = a;//单参数的构造函数可以进行隐式类型转换 A aa =1;//c++11中多参数的构造函数也可以进行隐式类型转换 B bb ={2,3};return0;}
运行结果如下,无报错成功
  1. 当然如果我们在类中不想要类型转换发生,可以使用explicit修饰对应成员函数即可限制类型转换
#include<iostream>usingnamespace std;classA{public:explicitA(int a):_a(a){}private:int _a;};classB{public:explicitB(int b,int c):_b(b),_c(c){}private:int _b;int _c;};intmain(){//隐式类型转换int a =0;double b = a;//单参数的构造函数可以进行隐式类型转换 A aa =1;//c++11中多参数的构造函数也可以进行隐式类型转换 B bb ={2,3};return0;}
运行结果如下

强制类型转换

  1. 显示的强制类型转换:需要用户使用 () 手动进行转换,强制类型转换近似关系的类型,例如指针和整形int之间,虽然指针和整形int是两种不同的类型,但是指针的本质是地址的编号,从小到大的地址进行编号,指针其实也是表示着数据的大小,整形int也是用于表示数据的大小,所以指针和整形int有近似关系,可以进行转换
intmain(){//强制类型转换int a =0;int* pa =nullptr; pa =(int*)a;return0;}
运行结果如下
  1. 当然如果是完全没有关系的两种类型那必然是不可以使用强制类型转换的,例如:string和vector之间是完全不相同的两种类型就不可以使用强制类型转换进行转换
intmain(){ string str; vector<int> v; str =(string)v;return0;}
运行结果如下,完全不同的类型无法进行强制类型转换

const常变量的强制类型转换

  1. 对于const修饰的变量称为常变量,它开了空间进行存储数据,由于被const进行修饰,所以变量具有常性,数据不可以进行修改,但是我们可以通过类型转换,通过地址解引用去修改它存储的数据的值,但是这种方式是不安全的,因为本质上我们使用const进行修饰变量,就是期望不进行修改数据
intmain(){//const常变量constint c =0;//这种转换方式是不安全的int* pc =(int*)&c;(*pc)++; cout << c << endl; cout <<*pc << endl;return0;}
运行结果如下
奇怪,小编不是取出常变量的地址,通过指针解引用的方式修改地址上存储的数据了吗?为什么这里的n和*pc的值却不一样,为什么n不是1?那么接下来小编调试带领大家看一下监视窗口,监视窗口显示的是实际内存中存储的值

那么继续执行语句,结果如下,???什么?在实际的内存中c修改成1了

那么继续执行语句,打印一下c,这你扯不扯,编译器给c打印的不是内存中的实际值,而是c最初值0

其实原因就是编译器认为这种取出常变量的地址,通过指针解引用的方式修改常变量的方式是不安全的,所以编译器对于访问打印常变量采用的方式是直接使用最初值进行替换或者常变量初始化的时候除了初始化常变量,并且还会一并将初始值放到寄存器中,当需要使用常变量的时候去寄存器中取常变量的初始值,不同的编译器实现可能不同
  1. 那么我们知道了这里为什么编译器打印c的值是初始值了,如果小编非要编译器取出变量的值的时候每次去内存里去取,那么就可以使用volatile关键字进行修饰变量,被volatile修饰的变量编译器在取它的值的时候,每次都会去内存中去取
intmain(){//const常变量volatileconstint c =0;//这种转换方式是不安全的int* pc =(int*)&c;(*pc)++; cout << c << endl; cout <<*pc << endl;return0;}
运行结果如下,编译器在取出常变量c的值的时候,去内存中去取c的值,而不是进行常变量初始值的替换或者去寄存器中去取常变量的初始值

总结

  1. c语言的转换很简单,但是仍然存在一些坑
  2. 隐式类型转换会出现精度缺失,程序如果不小心疏忽,会造成代码错误等
  3. 例如下面的函数fun本意是想要将形参的类型写成int,但是这里疏忽误写为了size_t,那么传参后,发生了我们未能察觉的隐式类型转换,while判断一直成立,如下代码就会陷入死循环
voidfun(size_t n){while(n >=0){ cout << n << endl; n--;}}intmain(){fun(5);return0;}
运行结果如下
  1. 显示类型转换,const类型转换和普通的强制类型转换不易区分
  2. 那么c++提出了自己的转换风格,可以有效的处理这些问题。由于c++兼容c语言,所以对于c++的转换只是一个建议,原有的c语言的转换仍然可以使用

二、c++的四种类型转换

c++引入了四种强制类型准换:分别是static_cast,reinterpret_cast,const_cast,dynamic_cast
下面小编将对这四种强制类型转换进行讲解

static_cast

static_cast也叫做静态转换,编译器执行的任何隐式类型转换都可以使用它,但是不能将它用于两种不相关的类型进行转换
intmain(){int a =0;double b =static_cast<double>(a);return0;}
运行结果如下,无误

reinterpret_cast

reinterpret_cast可以用于平替c语言中近似类型的强制类型转换,用于将一种类型强制转化为另一种近似关系的类型,但是reinterpret_cast不可以用于const类型的强制类型转换
intmain(){int a =0;int* pa =reinterpret_cast<int*>(a);return0;}
运行结果如下
intmain(){constint a =0;int* pa =reinterpret_cast<int*>(&a);return0;}
运行结果如下

const_cast

const_cast用于const类型的强制类型转换,用于去掉常变量的const属性
intmain(){constint a =0;int* pa =const_cast<int*>(&a);return0;}
运行结果如下

dynamic_cast

dynamic_cast也叫做动态转换,用于将父类对象的指针或引用转换为子类对象的指针或引用dynamic_cast只能用于父类含有虚函数的类(父类和子类构成多态)dynamic_cast会先检查能否转换成功,如果不能转换成功,则返回nullptr
  1. 向上转换:子类的指针或引用转化为父类的指针或引用,这种转换也叫做切割,切片,由于赋值兼容规则,这种转换是天然支持的,不需要进行类型转换
  2. 向下转换:父类的指针或引用转化为子类的指针或引用(使用dynamic_cast进行转化是安全的)
  3. 我们知道父类的指针或引用是有可能指向子类的指针或引用的,此时进行向下转化无非就是将原来的子类的指针或引用转化给子类的指针或引用,这完全是可以的,没有问题的,所以此时转化就是安全的,那么dynamic_cast就会允许转化,会返回转化后的类型的指针或引用
  4. 我们知道父类的指针或引用也有可能指向父类的指针或引用,此时进行向下转化,就会将父类的指针或引用转化为子类的指针或引用,此时由于父类的指针或引用指向的对象中没有子类那一部分的成员函数,如果进行赋值除了将父类的指针或引用指向的父类对象的那一部分成员函数之外,也会一并将未知内存的内容也给给子类的指针或引用,这可就坑了,子类的指针或引用可以接收父类对象的那一部分这没问题,但是如果接收了未知内存的内容,那么进行访问,或进行操作会造成越界访问或者崩溃,所以dynamic_cast认为这是不安全的,所以就不会允许进行转换,会返回一个nullptr,用户接收nullptr之后可以进行判断是否转化成功
  5. 为什么dynamic_cast可以进行检查?父类中有虚函数,子类继承父类,父类和子类构造多态,那么此时父类和子类就会有自己独特的一张虚函数表,编译器会在虚函数表中做一些独属于父类和子类自己的标记,dynamic_cast进行检查的时候会去对应对象的虚函数表中去检查这个标记来判断是这个对象指向的究竟是父类对象还是子类对象,进而就可以进行检查了
  6. 我们传入父类的指针,看能否进行转换
classA{public:virtualvoidf(){}};classB:publicA{};voidfun(A* a){ B* b =dynamic_cast<B*>(a);if(b ==nullptr){ cout <<"转换失败"<< endl;}else{ cout <<"转换成功"<< endl;}}intmain(){ A a;fun(&a);return0;}
运行结果如下
  1. 我们传入子类的指针,看能否进行转换
classA{public:virtualvoidf(){}};classB:publicA{};voidfun(A* a){ B* b =dynamic_cast<B*>(a);if(b ==nullptr){ cout <<"转换失败"<< endl;}else{ cout <<"转换成功"<< endl;}}intmain(){ B b;fun(&b);return0;}
运行结果如下

三、RTTI

RTTI(Run-time Type identification)也叫做运行时类型检查
c++通过以下几种方式都是运行时类型检查去支持RTTI
  1. typeid,用于打印变量的类型
  2. decltype,用于识别类型,进行定义变量
  3. dynamic_cast,用于将父类的指针或引用转化为子类的指针或引用

总结

以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!

Read more

Flutter for OpenHarmony:Flutter 三方库 bluez 玩转 Linux 风格的蓝牙操作(蓝牙底层互操作)

Flutter for OpenHarmony:Flutter 三方库 bluez 玩转 Linux 风格的蓝牙操作(蓝牙底层互操作)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net。 前言 随着鸿蒙(OpenHarmony)在工业互联网、智能座舱和物联网(IoT)领域的深入应用,与蓝牙设备的底层通信成为了许多开发者的刚需。在一些基于鸿蒙内核的特定工业版或车机版系统中,底层可能由于适配历史原因或分层设计,保留了类似 Linux 的 D-Bus 通信机制。 bluez 是一个专门用于与 Linux BlueZ 蓝牙协议栈通过 D-Bus 进行交互的 Dart 库。虽然对于普通的 HarmonyOS NEXT 手机开发我们通常使用官方的蓝牙插件,但在深度定制的鸿蒙发行版中,bluez 库为我们提供了一扇通往蓝牙底层控制的大门。 一、原理解析 / 概念介绍 1.1 基础概念 bluez 库并不直接操作蓝牙硬件,而是通过 D-Bus (Desktop Bus) 系统总线与系统级的蓝牙守护进程进行会话。 D-Bus

By Ne0inhk
Flutter 组件 substrate_bip39 的适配 鸿蒙Harmony 实战 - 驾驭区块链级 BIP39 安全底座、实现鸿蒙端私钥派生与国密级密钥保护方案

Flutter 组件 substrate_bip39 的适配 鸿蒙Harmony 实战 - 驾驭区块链级 BIP39 安全底座、实现鸿蒙端私钥派生与国密级密钥保护方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 substrate_bip39 的适配 鸿蒙Harmony 实战 - 驾驭区块链级 BIP39 安全底座、实现鸿蒙端私钥派生与国密级密钥保护方案 前言 在鸿蒙(OpenHarmony)生态向金融科技、Web3.0 以及受控安全办公领域深耕的过程中,“密钥管理(Key Management)”是所有信任链条的起点。面对“如何将助记词(Mnemonic)安全地转化为可用于签名的私钥”、“如何兼容 Polkadot/Substrate 这种具备高阶加密特性的异构账本协议”这些硬核问题,传统的 crypto 库往往力有不逮。 我们需要一种工业级、符合现代跨平台密码学标准(BIP39/Ed25519)的加密底座。 substrate_bip39 是基于 Substrate 框架裁剪出的高性能密钥派生引擎。

By Ne0inhk
Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换

Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 vy_string_utils 的鸿蒙化适配指南 - 实现高效的字符串模式校检、支持富文本清洗与多维度命名规范转换 前言 在进行 Flutter for OpenHarmony 开发时,字符串处理几乎无处不在。从校验用户输入的手机号,到将后台返回的 snake_case 字段转化为鸿蒙 UI 需要的文本格式,这类基础工作如果通过硬编码实现,会产生大量的冗余逻辑。vy_string_utils 是一款轻量级却功能强悍的字符串工具包。它通过一系列精心设计的扩展方法,让鸿蒙开发者能以极简的语法管理所有文本流。本文将带大家领略这款“字符串手术刀”的威力。 一、原理解析 / 概念介绍 1.1 基础原理 vy_string_utils 基于 Dart

By Ne0inhk
AIGC时代 | 如何从零开始学网页设计及3D编程

AIGC时代 | 如何从零开始学网页设计及3D编程

文章目录 * 一、网页设计入门 * 1. 基础知识 * 2. 学习平台与资源 * 3. 示例代码:简单的HTML+CSS+JavaScript网页 * 二、3D编程入门 * 1. 基础知识 * 2. 学习平台与资源 * 3. 示例代码:简单的Unity 3D游戏 * 《编程真好玩:从零开始学网页设计及3D编程》 * 内容简介 * 作者简介 * 目录 在AIGC(人工智能生成内容)时代,网页设计和3D编程成为了许多人的热门学习方向。无论你是希望成为一名网页开发者,还是想进入3D建模和动画领域,从零开始学习并掌握这些技能将为你打开许多机会的大门。本文将详细介绍如何从零开始学习网页设计及3D编程,并附上示例代码。 一、网页设计入门 1. 基础知识 网页设计主要包括前端和后端技术。前端技术主要关注用户界面的设计和实现,主要包括HTML、CSS和JavaScript。后端技术则负责处理服务器端的逻辑和数据处理,常见的后端语言包括Node.js、Python等。 2. 学习平台与资源

By Ne0inhk