C++中的四种强制类型转换符详解

前 言

C++ 既支持 C 风格的类型转换,又有自己风格的类型转换。C 风格的转换格式很简单,但是有不少缺点:

  • 转换太过随意,可以在任意类型之间转换。你可以把一个指向 const 对象的指针转换成指向非 const
    对象的指针,把一个指向基类对象的指针转换成一个派生类对象的指针,这些转换之间的差距是非常 巨大的,但是传统的C语言风格的类型转换没有区分这些。
  • C 风格的转换没有统一的关键字和标示符。对于大型系统,做代码排查时容易遗漏和忽略。

C++ 风格完美的解决了上面两个问题。

  • 对类型转换做了细分,提供了四种不同类型转换,以支持不同需求的转换;
  • 类型转换有了统一的标示符,利于代码排查和检视。

下面分别来介绍这四种转换:static_cast、dynamic_cast、const_cast、reinterpert_cast,它们都是类模板。

一、static_case(静态转换)

(1)使用场景

  • 在基本数据类型之间转换,如把 int 转换为 char,这种带来安全性问题由程序员来保证;
  • 在有类型指针与 void * 之间转换;
  • 用于类层次结构中基类和派生类之间指针或引用的转换。
    上行转换(派生类---->基类)是安全的;
    下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的。
    (2)使用特点
  • 主要执行非多态的转换操作,用于代替C中通常的转换操作。
  • 隐式转换都建议使用 static_cast 进行标明和替换。
  • 不能使用 static_cast 在有类型指针内转换。
    (3)示例程序如下所示:
#include <iostream>using namespace std;class CBase // 基类(父类)
{};class CDerived : public CBase  // 派生类(子类)
{};int main()
{// 1. 使用static_cast在基本数据类型之间转换float fval = 10.12;int ival = static_cast<int>(fval);  // float --> intcout << ival << endl;  // out: 10// 2. 使用static_cast在有类型指针与void *之间转换int *intp = &ival;void *voidp = static_cast<void *>(intp); // int* --> void*// cout << *voidp << endl; // error,voidp的大小未知long *longp = static_cast<long *>(voidp);cout << *longp << endl; // out: 10// 3. 用于类层次结构中基类和派生类之间指针或引用的转换// 上行转换(派生类---->基类)是安全的CDerived *tCDerived1 = nullptr;CBase *tCBase1 = static_cast<CBase*>(tCDerived1);// 下行转换(基类---- > 派生类)由于没有动态类型检查,所以是不安全的CBase *tCBase2 = nullptr;CDerived *tCDerived2 = static_cast<CDerived*>(tCBase2); //不会报错,但是不安全// 不能使用static_cast在有类型指针内转换float *floatp = &fval;  //10.12的addr//int *intp1 = static_cast<int *>(floatp); // error,不能使用static_cast在有类型指针内转换cout << *floatp << endl;    // out: 10.12
}/*
输出结果:10
10
10.12
*/

二、dynamic_cast(动态转换)

(1)使用场景

  • 用于将一个父类的指针/引用转化为子类的指针/引用(下行转换)。
    (2)使用特点
  • 基类必须要有虚函数,因为 dynamic_cast 是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中。
  • 对于下行转换,dynamic_cast 是安全的(当类型不一致时,转换过来的是空指针),而 static_cast 是不安全的。
  • 对指针进行 dynamic_cast,失败返回 NULL,成功返回正常 cast 后的对象指针;对引用进行
    dynamic_cast,失败抛出一个异常,成功返回正常 cast 后的对象引用。
    (3)示例程序如下所示:
#include <iostream>using namespace std;class CBase    // 基类(父类)
{
public:// dynamic_cast在将父类cast到子类时,父类必须要有虚函数virtual int test() { return 0; } // 一定要是 virtual
};class CDerived : public CBase  // 派生类(子类)
{
public:int test() { return 1; }
};int main()
{CBase *p_CBase = new CBase;  // 基类对象指针CDerived *p_CDerived = dynamic_cast<CDerived *>(p_CBase);  // 将基类对象指针类型转换为派生类对象指针CBase i_CBase;    // 创建基类对象CBase &r_CBase = i_CBase;    // 基类对象的引用CDerived &r_CDerived = dynamic_cast<CDerived &>(r_CBase);  // 将基类对象的引用转换派生类对象的引用
}

三、const_cast(常量转换)

(1)使用场景

  • 常量指针(或引用)与非常量指针(或引用)之间的转换。
    (2)使用特点
  • cosnt_cast 是四种类型转换符中唯一可以对常量进行操作的转换符。
  • 去除常量性是一个危险的动作,尽量避免使用。
    (3)示例程序如下所示:
#include <iostream>using namespace std;int main()
{int value = 100;const int *cpi = &value; // 定义一个常量指针//*cpi = 200;   // 不能通过常量指针修改值// 1. 将常量指针转换为非常量指针,然后可以修改常量指针指向变量的值int *pi = const_cast<int *>(cpi);*pi = 200;// 2. 将非常量指针转换为常量指针const int *cpi2 = const_cast<const int *>(pi); // *cpi2 = 300;  //已经是常量指针const int value1 = 500;const int &c_value1 = value1; // 定义一个常量引用// 3. 将常量引用转换为非常量引用int &r_value1 = const_cast<int &>(c_value1);// 4. 将非常量引用转换为常量引用const int &c_value2 = const_cast<const int &>(r_value1);
}

四、reinterpret_cast(不相关类型的转换)

reinterpret 的英文含义有重新转换的含义,就相当于 C 语言中不相关类型的转换,强转。
(1)使用场景

  • 用在任意指针(或引用)类型之间的转换。
  • 能够将整型转换为指针,也可以把指针转换为整型或数组。
    (2)使用特点
  • reinterpret_cast 是从底层对数据进行重新解释,依赖具体的平台,可移植性差。
  • 不到万不得已,不用使用这个转换符,高危操作。
    (3)示例程序如下所示:
#include <iostream>using namespace std;int main()
{int value = 100;// 1. 用在任意指针(或引用)类型之间的转换double *pd = reinterpret_cast<double *>(&value);cout << "*pd = " << *pd << endl;// 2. reinterpret_cast能够将指针值转化为整形值int *pv = &value;int pvaddr = reinterpret_cast<int>(pv);cout << "pvaddr = " << hex << pvaddr << endl;cout << "pv = " << pv << endl;
}/*
输出结果:*pd = -9.25596e+61
pvaddr = 8ffe60
pv = 008FFE60
*/

五、扩展

下面程序中,参数 pb 指向的是 B 类对象,pd1 的值不为0,而 pd2 的值为 0。

#include <iostream>using namespace std;class B
{int m_iNum;virtual void foo() {};
};
class D:public B
{char *m_szName[100];
};void func(B* pb)
{D *pd1 = static_cast<D *>(pb);D *pd2 = dynamic_cast<D *>(pb);cout << pd1 << endl; //00CFF7C0cout << pd2 << endl; //00000000    
}int main()
{B pb; //父类对象pbcout << "&pb: " << &pb << endl; //&pb: 00CFF7C0    func(&pb);return 0;
}/*
输出结果:&pb: 00CFF7C0
00CFF7C0
00000000
*/

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

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

相关文章

vue-cli搭建过程(HBuilder X搭建)

vue.js:前端主流框架&#xff08;对某一方面技术完整的封装&#xff0c;是一套完善的解决方案&#xff09; vue-cli搭建项目&#xff08;官方提供脚手架&#xff09; vue脚手架&#xff1a;是一套项目搭建的快捷方式&#xff0c;可以将项目中的依赖集成进来&#xff0c;生成统…

MongoDB快速上手

文章目录 1、mongodb相关概念1.1、业务应用场景1.2、MongoDB简介1.3、体系结构1.3.1 数据库 (databases) 管理语法1.3.2 集合 (collection) 管理语法 1.4、数据模型1.5、MongoDB的特点 2、单机部署3、基本常用命令3.1、案例需求3.2、数据库操作3.2.1 选择和创建数据库3.2.2 数据…

新一代网络框架UringNet,基于最新的异步I/O

介绍 在去年的一篇文章中&#xff0c;笔者曾经提到了最新一代的网络I/O框架UringNet。具体内容可以参考Rings’ Power,性能“世界第一”的Web I/O框架。这是基于最新Linux内核的异步I/O组件io_uring开发的网络框架。由于采用了最新的异步框架&#xff0c;因此在同等硬件配置条件…

漏洞扫描环境:win10系统用VMware Workstation打开虚拟机若干问题

win10系统用VMware Workstation打开虚拟机若干问题 一 .VMware打开虚拟机就蓝屏重启怎么解决&#xff1f;一. VMware打开虚拟机就蓝屏重启怎么解决&#xff1f;方法一&#xff1a;1、同时按下CTRLSHIFTESC打开任务管理器功能&#xff0c;之后依次点击-详细信息-性能后出现下列界…

【GPU常见概念】GPU常见概念及分类简述

随着大模型和人工智能的爆火&#xff0c;大家对GPU的关注持续上升&#xff0c;本文简单简述下GPU经常用的概念。 GPU&#xff08;图形处理器&#xff09;&#xff0c;又称显示核心、视觉处理器、显示芯片&#xff0c;是一种专门在个人电脑、工作站、游戏机和一些移动设备&…

k8s 拉取镜像报错 no basic auth credentials

文章目录 [toc]基于现有凭据创建 Secret通过命令行创建 Secretpod 使用指定 secret 认证私有镜像仓库 省流提醒&#xff1a; 本次解决的问题是 docker login 可以正常登录&#xff0c;docker pull 也可以正常拉取镜像&#xff0c;只是 k8s 在启动 pod 的时候&#xff0c;没有指…

【Java-LangChain:面向开发者的提示工程-3】迭代优化

第三章 迭代优化 当使用 LLM 构建应用程序时&#xff0c;实践层面上很难第一次尝试就成功获得适合最终应用的 Prompt。但这并不重要&#xff0c;只要您有一个好的迭代过程来不断改进您的 Prompt&#xff0c;那么您就能够得到一个适合任务的 Prompt。虽然相比训练机器学习模型&…

辅助驾驶功能开发-测试篇(2)-真值系统介绍

1 真值系统概述 1.1 真值评测系统核心应用 快速构建有效感知真值,快速完成感知性能评估,快速分析感知性能缺陷。 主要应用场景包括: 1. 感知算法开发验证: 在算法开发周期中,评测结果可以作为测试报告的一部分,体现算法性能的提升。 2. 遴选供应…

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景&#xff1f;34、互斥量能不能在进程中使用&#xff1f;35、协程是轻量级线程&#xff0c;轻量级表现在哪里&#xff1f;36、说说常见信号有哪些&#xff0c;表示什么含义&#xff1f;37、说说线程间通信的方式有哪些&#xff1f;38、说说…

关于使用 uniapp Vue3 开发分享页面 语法糖 setup 开发获取ref踩坑

上代码 前端代码 <!-- 分享弹出 --> <uni-popup ref"share" type"share" safeArea backgroundColor"#fff"><uni-popup-share></uni-popup-share> </uni-popup>处理函数 import {onNavigationBarButtonTap} from…

点击router-link时候会发生什么?

当你点击链接或按钮时&#xff0c;将会导航到 User 组件&#xff0c;就会显示相应的用户 ID。 这里说一下执行流程&#xff0c;当点击一个 router-link 时&#xff0c;Vue Router会执行以下流程&#xff1a; 1&#xff09;点击事件触发: 当你点击 router-link 组件时&#xf…

【Java 进阶篇】JDBC查询操作详解

在数据库编程中&#xff0c;查询是一项非常常见且重要的操作。JDBC&#xff08;Java Database Connectivity&#xff09;提供了丰富的API来执行各种类型的查询操作。本篇博客将详细介绍如何使用JDBC进行查询操作&#xff0c;包括连接数据库、创建查询语句、执行查询、处理结果集…

python——Django框架

一、基本介绍 Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。 使用 Django&#xff0c;只要很少的代码&#xff0c;Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容&#xff0c;并进一步开发出全功能的 Web 服务 Django 本身基于 MVC …

AttributeError: module ‘dgl‘ has no attribute ‘batch_hetero‘

DGLWarning: From v0.5, DGLHeteroGraph is merged into DGLGraph. You can safely replace dgl.batch_hetero with dgl.batch

MySQL索引:结构、语法、分类和优化

MySQL索引是数据库中非常关键的性能优化手段。它们提供了快速访问数据的方法&#xff0c;同时也可以极大地提高查询效率。本文将深入介绍MySQL索引的结构、语法、分类&#xff0c;以及如何使用Profile和EXPLAIN来优化查询性能&#xff0c;带有详细的实例演示。 索引结构 MySQ…

【Pytorch笔记】4.梯度计算

深度之眼官方账号 - 01-04-mp4-计算图与动态图机制 前置知识&#xff1a;计算图 可以参考我的笔记&#xff1a; 【学习笔记】计算机视觉与深度学习(2.全连接神经网络) 计算图 以这棵计算图为例。这个计算图中&#xff0c;叶子节点为x和w。 import torchw torch.tensor([1.]…

SpringMVC的@InitBinder的作用

一、前言 在SpringMVC中&#xff0c;InitBinder注解用于自定义数据绑定的方法。在使用表单提交数据时&#xff0c;SpringMVC会将请求参数绑定到Java对象中。但是&#xff0c;有些时候请求参数的数据格式可能与Java对象的属性格式不一致&#xff0c;这时就需要使用InitBinder注…

JavaScript内置对象 - Array数组(四)- 序列生成器

序列生成器是生成一个指定起始值和结束值的序列&#xff0c;并且根据指定间隔长度&#xff0c;生成序列数组。 完成此功能需要使用到Array内置对象的from()对象&#xff0c;以及类数组相关知识&#xff0c;前面几篇有相关案例进行演示。 地址一&#xff1a;JavaScript内置对象…

基于.Net Core实现自定义皮肤WidForm窗口

前言 今天一起来实现基于.Net Core、Windows Form实现自定义窗口皮肤&#xff0c;并实现窗口移动功能。 素材 准备素材&#xff1a;边框、标题栏、关闭按钮图标。 窗体设计 1、创建Window窗体项目 2、窗体设计 拖拉4个Panel控件&#xff0c;分别用于&#xff1a;标题栏、关…

【Redis】基础数据结构-字典

Redis 字典 基本语法 字典是Redis中的一种数据结构&#xff0c;底层使用哈希表实现&#xff0c;一个哈希表中可以存储多个键值对&#xff0c;它的语法如下&#xff0c;其中KEY为键&#xff0c;field和value为值&#xff08;也是一个键值对&#xff09;&#xff1a; HSET key…