【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

面试官问 C++ 多态?虚函数重写 + 虚表指针 + 动态绑定,核心考点全覆盖

面试官问 C++ 多态?虚函数重写 + 虚表指针 + 动态绑定,核心考点全覆盖

✨ 孤廖:个人主页 🎯 个人专栏:《C++:从代码到机器》 🎯 个人专栏:《Linux系统探幽:从入门到内核》 🎯 个人专栏:《算法磨剑:用C++思考的艺术》 折而不挠,中不为下 文章目录 * 正文: * 1. 多态的概念 * 2. 多态的定义及实现 * 2.1 虚函数 * 2.2 虚函数的重写/覆盖 * 2.3 虚函数重写的⼀些其他问题 * 2.4 override 和 final关键字 * 2.5 重载/重写/隐藏的对⽐ * 3. 纯虚函数和抽象类 * 4. 多态的原理 * 4.1 多态是如何实现的 * 4.2

By Ne0inhk

STL 容器:vector 动态数组的全面解析

STL 容器:vector 动态数组的全面解析 在C++ STL的序列式容器中,vector无疑是最常用、最灵活、最贴合日常开发需求的容器之一。无论是笔试面试中的算法题,还是实际项目中的数据存储,vector都能凭借其“动态扩容”“随机访问”的核心优势,成为开发者的首选。 很多开发者对vector的认知停留在“可以自动变长的数组”,但其实它的底层实现、扩容机制、接口用法以及性能优化,都有很多值得深究的细节。今天这篇博客,我们就来全面拆解vector——从基础用法到底层原理,从实操示例到避坑指南,让你真正吃透这个STL“高频容器”,做到灵活运用、规避隐患。 一、vector 是什么?核心定位与优势 vector 是 STL 标准模板库中的序列式容器,底层基于动态数组实现,本质是封装了动态分配内存的模板类。它可以存储任意数据类型(内置类型int、float,自定义结构体、类等),支持动态扩容(无需手动管理内存大小),同时提供了便捷的接口,实现数据的增、删、

By Ne0inhk
C++寻位映射的究极密码:哈希扩展

C++寻位映射的究极密码:哈希扩展

文章目录 * 1.位图 * 1.1 位图的结构 * 1.2 位图映射的比特位标记成1 * 1.3 位图映射的比特位标记成0 * 1.4 位图映射判断为1 or 0 * 2.布隆过滤器 * 2.1 布隆过滤器的结构 * 2.2 布隆过滤器的哈希函数 * 2.3 布隆过滤器的插入 * 2.4 布隆过滤器映射判断为true or false * 2.5 布隆过滤器的优缺点 * 3.常见面试题 * 3.1 哈希切割 * 3.1.1 问题一 * 3.1.2 问题二 * 3.

By Ne0inhk
Rust赋能Android蓝牙协议栈:从C++到安全高效的重构之路

Rust赋能Android蓝牙协议栈:从C++到安全高效的重构之路

在移动设备生态中,蓝牙协议栈是连接物理世界与数字世界的关键桥梁,从无线耳机、智能手环到车载系统,其稳定性、安全性与效率直接决定用户体验。长期以来,Android蓝牙协议栈核心模块基于C++开发,凭借接近硬件的性能优势支撑了数十亿设备的运行。但随着物联网设备爆发式增长、蓝牙5.3/5.4等新协议落地,C++固有的内存安全缺陷与并发管理难题愈发凸显。2021年起,Google开始在Android蓝牙协议栈中引入Rust重构核心模块,这一技术选型并非偶然,而是工程实践中安全与效率平衡的必然结果。 目录 一、Android蓝牙协议栈的C++之困 1.1 内存安全漏洞:蓝牙模块的阿喀琉斯之踵 1.2 并发管理复杂:多设备连接下的稳定性难题 1.3 代码可维护性下降:遗产代码的演进瓶颈 二、Rust:破解困局的关键特性赋能 2.1 所有权模型 2.2 并发安全:无数据竞争的天生优势 2.3 零成本抽象与可维护性:

By Ne0inhk