【C++】C++提供类型转换的机制

news2024/10/4 4:18:45

目录

前言:

一,static_cast

二,reinterpret_cast

三,const_cast

四,dynamic_cast


前言:

        传统的不同类型转换有隐式类型转换(类型不匹配时编译器自动进行的转换,如:int a;float b; a = b;)和强制类型转换(强制类型转换也叫显示类型转换,如:a = (int)b)。

        隐式类型转换:基本数据类型之间的转换。内置类型之间的隐式转换系统内部自动完成,但必须是两者相近的类型,如int,char,double等。自定义类型之间的隐式转换借助的是构造函数来完成。内置类型隐式转换成自定义类型也是借助的构造函数完成。自定义类型隐式转成内置类型借助的是一个重载类型的函数,即:operator 类型。 

class B
{
public:
    int _b = 10;
};
class A
{
public:
    A(int a = 0) :_a(a) {    }
    A(const B& b) :_a(b._b) {    }
    //重载一个隐式转换成int型的函数
    operator int() 
    {
        return _a + 1;
    }
    int _a;
};

int main()
{
    int x = 5;
    double f = 3.14;
    A a;
    B b;
    a = x; //内置类型隐式转换成自定义类型
    cout << "A: " << a._a << "  " << "x: " << x << endl;
    a = b; //自定义类型隐式转换成自定义类型
    cout << "A: " << a._a << "  " << "B: " << b._b << endl;
    x = a; //自定义类型隐式转换成内置类型
    cout << "x: " << x << "  " << "A: " << a._a << endl;
    x = f; //内置类型之间的隐式类型转换,系统内部自动实现
    cout << "x: " << x << "  " << "f: " << f << endl;
    return 0;
}

        强制类型转换:指名要转换的类型后系统内部强制进行机制转换,比如指针、整数和函数指针之间,隐式转换很多情况下都无法完成。这种转化机制比较强大,即便相似度较小也能完成转换, 但若是几乎没有什么相似度,那么也无法完成。一般用于特殊说明类型或针对于指针的情况

int main()
{
    int a = 1;
    double f = 3.14;
    char c = 'a';

    const char* str = "aaa";

    //int* p = &f; 错误,不能隐式转换
    int* p = (int*)&f;
    //a = &c; 错误,不能隐式转换
    a = (int)&c;
    //p = a; 错误,不能隐式转换
    p = (int*)a;

 

    //a = (float)str; 错误,const char*与float基本没有相似度,无法强制转换
    return 0;
}

        隐式类型的转换主要是基于相似度较大的数据之间,通常不适用于指针。强制类型转换范围较广,可以实现有关指针转换的情况。但无论是哪种转换,这里面都存在不少的缺点。隐式类型转化有些情况下可能会出现数据精度丢失,可维护性和可读性差,而显式类型转换将所有情况混合在一起,代码不够清晰,难以发觉。因此C++提出了四种类型转换操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast。它们分别拥有自己的风格。


一,static_cast

        static_cast类型转换运算符基本等价于以前的隐式类型转换,即主要用于基本数据类型之间的隐式类型转换,不能用于指针、整数和函数指针之间的强制转换。为了预防隐式类型转化可能出现数据丢失的情况,C++提供了关键字static_cast来明确这里正在进行隐式转换,使用于需要明确隐式转换的地方,即将隐式转换显式化表示出来。它是一个更安全的类型转换运算符。

int main()
{
    double d = 12.34;
    int a1 = static_cast<int>(d); //将d隐式转换成int型,然后赋值a1
    cout << a1 << endl;
    //上面a1的static_cast<int>(d)隐式转换与下面a2对应
    int a2 = d; 
    cout << a2 << endl;

    int* p = &a1;
    //int a3 = static_cast<int>(p); 错误,相当于a3 = p;

    //int* p1 = static_cast<int*>(a1); 错误,相当于int* p1 = a1;
    return 0;
}


二,reinterpret_cast

        reinterpret_cast操作符类似于以前强制类型转换,但又不能单纯理解为以前的强制类型转换,适用于static_cast隐式转换的它就不适用,因为它提供了更多的类型安全性。总的来说,reinterpret_cast操作符适用于隐式转化以外的强制转化,即reinterpret_cast用于指针、整数和函数指针之间的转换,而不是基本数据类型之间的隐式转换。

int main()
{
    double f = 3.14;
    int i = 1;
    int* pi = &i;
    double* pf = &f;    
    //static_cast与reinterpret_cast的正确使用
    int a = static_cast<int>(f);
    float f1 = static_cast<float>(i);
    int a1 = reinterpret_cast<int>(pi);  //对应int a1 = (int)pi;
    int a2 = reinterpret_cast<int>(pf);  //对应int a2 = (int)pf;

    

    //下面都是错误的使用
    int a = reinterpret_cast<int>(f); //错误,两数据间可以进行隐式转换了, 但 a = (int)f 正确
    float f1 = reinterpret_cast<float>(i); //错误,同理,传统的 f1 = (float)i 正确
    int a1 = static_cast<int>(pi);        //错误,pi不能隐式转换成int型
    int a2 = static_cast<int>(pf);       //错误,同理
    return 0;
}

        reinterpret_cast也不是特别安全,因为它对应的是强制转换,这就导致它很容易被误用而引发错误或其它行为,比如子类指针强转为父类指针,这就可能导致父类指针指向子类对象出现越界访问的情况。因此,我们应特别小心使用reinterpret_cast,大多数情况下应该优先使用其它类型转换操作符,它们提供了更多的安全性。

        需要适用隐式类型转换的时候要用static_cast,隐式类型不能转换的且需要强制类型转换的要用reinterpret_cast,这里的强制转换不完全等同于传统的强制类型转换,可以理解为被限制的传统强制类型转换。


三,const_cast

        我们先来观察下面代码的问题。

int main()
{
    const int a1 = 2;
    int* p1 = (int*)&a1;
    *p1 = 3;
    cout << a1 << endl;
    cout << *p1 << endl;
    return 0;
}

输出窗口: 

 监视窗口:

        这里发现输出a1与p1所对应的数据不一样,但通过调试,监视窗口数据都正常。出现这种问题源于编译器对const数据的优化。由于const型数据不会改变,这里会把a1存入寄存器中,输出打印时把数据从寄存器中取出,但我们通过指针p1将内存中a1进行改变,监视窗口是从内存中读取的数据,这也就是输出打印时a1数据不变,监视窗口a1正常改变的原因。

        C++提供了关键字volatile 解决这块问题。volatile关键字告诉编译器某个变量的值可能在程序的执行过程中被外部因素修改。因此,volatile 处理变量后,数据不会往寄存器中存取,编译器总是从内存中读取实际值,而不是假设它可以在寄存器中缓存并重复使用,如:volatile const int a1 = 2,此时跟预料一样输出。

        const_cast转换也是类似于强制转换,与static_cast不同的是它常用来删除变量的const属性,不适用其它相关的强制转换。专门把const属性单独拿出来就是专门提醒去掉const是有一些内存可见优化的风险,要注意是否加了volatile,即上面那种情况。

int main()
{
    volatile const int a1 = 2;
    int* p1 = const_cast<int*>(&a1); //与int* p1 = (int*)&a1类似
    *p1 = 3;

    //若没有volatile修饰,这里输出的a1是2
    cout << a1 << endl;
    cout << *p1 << endl;

    //const_cast与reinterpret_cast对比
    int a2 = 1;
    //int p2 = const_cast<int>(p1); 错误,const_cast中的类型必须是指针、引用,
    int p2 = reinterpret_cast<int>(p1); //属于reinterpret_cast的强制类型转换,正确
    //int* p3 = const_cast<int*>(a2); 错误, const_cast只能去除const常性, 不能更改其类型
    int* p3 = reinterpret_cast<int*>(a2); //正确,与上同理
    return 0;
}


四,dynamic_cast

        dynamic_cast主要用于继承体系,解决将一个父类对象的指针或引用转换为子类对象的指针或引用(向下转型)出现的问题。

        向上转型:子类对象指针或引用转换成父类指针或引用(不需要转换,赋值兼容规则),即子类指针或引用赋值给符父类指针或引用。

        向下转型:父类对象指针或引用转换成子类指针或引用(用dynamic_cast转型是安全的),即父类指针或引用赋值给子类指针或引用。

        继承体系中,父类对象无论如何都不可能赋值子类对象,但父类指针或引用可以指向子类对象,这时,倘若单纯访问父类数据是没有任何问题的,但若是访问子类对象中的数据,由于父类指针或引用指向的只是包含父类这块空间的数据,没有包含子类的数据,所以这里会出现越界访问,即向下转型出现的问题。向上转型中,子类指针或引用指向更大的空间,赋值给父类指针或引用后,父类指针或引用虽然只能访问父类数据,但是这里没有出现越界访问,不存在安全隐患。总的来说,向上转型是从大空间转到小空间,可以正常进行;向下转型是从小空间转到大空间,会出现越界访问。

        当发生向上转型或向下转型时,dynamic_cast操作符会进行检查,若是向下转型,他将返回空指针,转型失败;若是向上转型或其它正常情况,将正常返回,转型成功。

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 = 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;
    A* pa = &a;
    B* pb = &b;
    //向上转型(子类指针赋值给父类指针)
    A* ppa = dynamic_cast<A*>(pb);
    if (ppa)
    {
        cout << "转换成功" << endl;
    }
    else
    {
        cout << "转换失败" << endl;
    }


    //向下转型(父类指针赋值给子类指针),ppb访问_b时出现越界访问,pa指向的空间中没有_b那块空间
    B* ppb = dynamic_cast<B*>(pa);    
    if (ppb)
    {
        cout << "转换成功" << endl;
    }
    else
    {
        cout << "转换失败" << endl;
    }
    
    fun(&a);
    fun(&b);
    return 0;
}

        注意:dynamic_cast只能用于父类含有虚函数的类,没有虚函数会报错,因为它是借助虚表来识别父子类。


总结:

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

        最后要说明,static_cast、reinterpret_cast、const_cast、dynamic_cast都是编程中的规范,它们各自对类型转换的限制有助于减少因误用类型转换而导致的错误,虽然它们也存在一定的安全隐患,但用此操作符特意明确这里存在类型转换,使我们能够意识到这里在进行对应功能的类型转换,方便对其操作。

        编程规范没有强制规定,但建议平常运用时养成编程规范的习惯。求职的面试过程中,常问的这块内容是:1,C++中的4种类型转化分别是哪几种?     2,说说4种类型转化的应用场景。

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

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

相关文章

记一次安卓.apk加固,加固后安装失败,重新签名也安装失败问题

1、AndroidStudio打包生成.apk文件 2、使用360加固apk&#xff08;或其他平台&#xff09; 注意&#xff1a;加固后的apk必须进行重新签名才能安装&#xff0c;否则安装失败。apk签名可以使用jarsigner 和 apksigner&#xff0c;jarsigner 只能进行v1签名&#xff1b;apksigner…

Linux ip命令常用操作

ip 命令来自 iproute2 软件包&#xff0c;在 CentOS 7 中默认已安装&#xff08;yum install -y iproute&#xff09;。 iproute2 软件包提供了很多命令&#xff08;rpm -ql iproute |grep bin&#xff09;&#xff0c;如 ss 命令、bridge&#xff0c;这些命令可以完全替代 if…

【CMake系列】06-项目结构与输出路径管理

为了对大型项目实现更好的管理【模块化协作开发等等】&#xff0c;cmake 提供了很多指令&#xff0c;可以对项目的结构进行调整、管理&#xff0c;便于项目的合理规划。本文我们要学习的就是 项目结构的设置&#xff0c;以及 构建程序等 输出路径的设置 本专栏的实践代码全部放…

【MySQL】Linux安装MySQL

一、center OS环境准备 为了在Linux系统中查看MySQL5.8与8.0版本的区别 我们要准备两个虚拟机&#xff0c;需要的软件&#xff1a;VMware和CentOS7 因为博主之前在学习redis的时候已经安装过一个虚拟机了&#xff0c;所以我就直接克隆了一个CentOS2.0 修改mac地址&#xff0…

mysql8.0中的mysql.ibd

mysql8.0版本中多了一个mysql.ibd的文件。5.7版本则没有这个文件。 MySQL5.7: .frm文件 存放表结构信息 .opt文件&#xff0c;记录了每个库的一些基本 信息&#xff0c;包括库的字符集等信息 .TRN&#xff0c;.TRG文件用于存放触发器的信 息内容。 在MySQL 8.0之前&#xff0…

Windows10系统中安装与配置PyTorch(无GPU版本)

文章目录 1. 什么是PyTorch2. PyTorch的安装与配置&#xff08;无GPU&#xff09;2.1 创建环境2.2 安装pytorch库&#xff08;无GPU&#xff09;2.3 验证安装结果 1. 什么是PyTorch PyTorch 是一种用于构建深度学习模型且功能完备的开源框架&#xff0c;通常用于处理图像识别和…

计算机网络学习实践:模拟RIP动态路由

计算机网络学习实践&#xff1a;模拟RIP动态路由 模拟动态路由RIP协议 1.实验准备 实验环境&#xff1a;华为模拟器ENSP 实验设备&#xff1a; 3个路由器&#xff0c;3个二层交换机&#xff08;不是三层的&#xff09;&#xff0c;3个PC机 5个网段 192.168.1.0 255.255.…

【C++】STL:栈和队列模拟实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

spring boot sso

代码&#xff1a;https://gitee.com/forgot940629/ssov2 授权服务 登录成功后&#xff0c;session中会存储UsernamePasswordAuthenticationToken&#xff0c;之后每次请求code时都会用UsernamePasswordAuthenticationToken生成OAuth2Authentication&#xff0c;并将OAuth2Aut…

【远程连接服务器】—— Workbench和Xshell远程连接阿里云服务器失败和运行Xshell报错找不到 MSVCP110.d的问题分析及解决

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、远程连接不上服务器1. Workbench远程连接失败2.Xshell也连接不上3.解决方法(1)问题描述&#xff1a;(2)解决&#xff1a; 4.再次连接服务器 二、运行Xshell…

SpringBoot 基于jedis实现Codis高可用访问

codis与redis的关系 codis与redis之间关系就是codis是基于多个redis实例做了一层路由层来进行数据的路由&#xff0c;每个redis实例承担一定的数据分片。 codis作为开源产品&#xff0c;可以很直观的展示出codis运维成本低&#xff0c;扩容平滑最核心的优势. 其中&#xff0…

算法第三天力扣第69题:X的平方根

69. x 的平方根 (可点击下面链接或复制网址进行做题) https://leetcode.cn/problems/sqrtx/https://leetcode.cn/problems/sqrtx/ 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被…

【实战JVM】-实战篇-06-GC调优

文章目录 1 GC调优概述1.1 调优指标1.1.1 吞吐量1.1.2 延迟1.1.3 内存使用量 2 GC调优方法2.1 发现问题2.1.1 jstat工具2.1.2 visualvm插件2.1.3 PrometheusGrafana2.1.4 GC Viewer2.1.5 GCeasy 2.2 常见GC模式2.2.1 正常情况2.2.2 缓存对象过多2.2.3 内存泄漏2.2.4 持续FullGC…

GAT1399协议分析(六)--校时

一、官方消息定义 DeviceIDType &#xff1a;GA/T1400.1,采集设备、 卡口点位、 采集系统、分析系统、视图库、应用平台等设备编码规则 TimeCorrectModeType&#xff1a; dateTime时间格式&#xff1a; TimeZone&#xff1a;时区&#xff0c;GAT1400里面没有找到具体内容&…

大家都在用的4款超实用视频剪辑软件,快来码住自用吧!

随着自媒体行业的不断发展&#xff0c;不少小伙伴也逐渐步入了短视频的热潮。对于短视频制作来说&#xff0c;视频剪辑软件的选择非常重要。 如果剪辑软件不够好&#xff0c;整个视频就基本垮掉了。今天就给大家推荐4款好用的视频剪辑软件。 1.牛学长视频剪辑 推荐剪辑新手入门…

重复文件查找?6款电脑重复文件清理软件很靠谱!

在日常使用电脑过程中&#xff0c;很多人下载文件后常常会忘记它们的存在&#xff0c;导致同一份资料在系统中存在多个副本。虽然你可以手动删除 Windows 系统中的所有重复文件&#xff0c;但这样做很费时间&#xff0c;而且有可能会遗漏很多文件。 而且随着重复文件的不断累积…

树莓集团:引领摄影培训,打造行业人才培育高地

在当今数字化快速发展的时代&#xff0c;摄影技术作为文化创意产业的重要组成部分&#xff0c;其人才培养显得尤为重要。依托其深厚的产业背景和丰富教育资源的树莓集团&#xff0c;倾力打造一流的摄影培训体系&#xff0c;为行业输送源源不断的高素质人才。 树莓集团旗下的摄…

JAVA技术设计模式

设计模式结构图 设计原则 职责单一原则接口隔离原则 一个类对另一个类的依赖应该建立在最小的接口上 依赖倒置面向接口编程,参数或变量,依赖注入,使用父类 开闭原则 对扩展开放(对提供方),对修改关闭(对使用方) 用抽象构建框架,用实现扩展细节 里氏替换原则…

汇凯金业:贵金属交易规则有哪些

贵金属投资目前非常火热&#xff0c;许多投资者从中获得了可观的收益。新手投资者在进入贵金属市场及其交易之前&#xff0c;务必要了解清楚贵金属的交易规则。了解规则和差异能帮助新手更好地进行贵金属投资交易。下面我们以现货类贵金属为例&#xff0c;详细说明贵金属的交易…

【活动通知 — 线上 Meetup】:ES|QL 及 B 站 Elasticsearch 平台实践 - 6 月 19 日

会议时间 2024年6月19日 19:00 — 21:00 参与方式 线上直播&#xff1a;Elastic 中国社区官方博客&#xff0c;elasticstack B 站号。链接请参考下面的报名地址。 活动议程 19:00-19:50 主题演讲&#xff1a;Elasticsearch 简单而高效的管道查询语言 - ES|QL 讲师&#xff…