十(1). 强制类型转换

news2025/12/15 23:42:24

继第十部分C++强制类型转换的四种方式,再进行强化巩固一下知识点

static_cast 最常用的,在指针之间做转换

const_cast 去除常量属性

dynamic_cast 用在基类和派生类之间的转换

reinterpret_cast 在任意类型之间进行转

10.1 static_cast

常见的使用场景:

  1. 基本数据类型之间的转换

    static_cast 可以用于将基本数据类型(如 intfloatchar 等)之间进行转换。

int i = 10;
float f = static_cast<float>(i); // 将 int 转换为 float
  2. 指针或引用类型之间的转换
  • 如果有指向基类和派生类的指针或引用,static_cast 可以用于指针或引用的类型转换。在类的层次结构中进行转换时,static_cast 主要用于向上或向下转换。

  • 向上转换:基类指针可以安全地转换为派生类指针(如果没有虚函数等特殊情况)。

  • 向下转换:派生类指针可以转换为基类指针,但如果不确认对象的实际类型,可能会产生不安全的转换。为了保证安全性,可以使用 dynamic_cast 进行运行时类型检查。

class Base {
public:
    virtual void show() { std::cout << "Base\n"; }
};
   
class Derived : public Base {
public:
    void show() override { std::cout << "Derived\n"; }
};
   
Base* basePtr = new Derived();
Derived* derivedPtr = static_cast<Derived*>(basePtr); // 向下转换
derivedPtr->show(); // 调用 Derived 的 show 方法
  3.  void* 指针转换为具体类型的指针

void* 是通用指针类型,static_cast 可以将其转换为具体的指针类型。

void* ptr = malloc(sizeof(int));
int* intPtr = static_cast<int*>(ptr); // 将 void* 转换为 int* 类型
*intPtr = 100;

4.转换为枚举类型

static_cast 可以用于将整数类型转换为枚举类型。

enum Color { Red, Green, Blue };
      
int colorCode = 1;
Color color = static_cast<Color>(colorCode); // 将整数转换为枚举类型
5. 去除掉常量/volatile 属性

static_cast 也可以用于去除类型的 constvolatile 属性,但这通常需要确保你没有破坏对象的常量性。

const int i = 10;
int* ptr = static_cast<int*>(const_cast<int*>(&i)); // 去除 const 属性
6. 转换为 nullptr_t(空指针类型)

可以将指针转换为 nullptr_t 类型(通常不常用)。

int* ptr = nullptr;
nullptr_t nullPtr = static_cast<std::nullptr_t>(ptr);  // 显式转换为空指针类型

// static_cast 是 C++ 中的一种类型转换操作符,用于在编译时进行类型转换。它通常用于在不同类型之间进行显式转换,特别是当你知道转换是安全的时。static_cast 适用于大多数常见的类型转换,比如基本类型之间的转换、类层次结构中的转换等。

10.2 dynamic_cast

dynamic_cast 是 C++ 中用于在类层次结构中进行安全的类型转换操作符。它与 static_cast 不同,dynamic_cast 主要用于执行运行时类型检查,尤其在涉及类继承关系的转换时,确保转换是安全的。

dynamic_cast 主要用于:

  1. 将基类指针或引用转换为派生类指针或引用(通常是向下转换),并进行运行时检查。

  2. 用于多态类型,即类具有虚函数时。

基本语法

dynamic_cast<目标类型>(表达式)
  • 目标类型:你希望转换成的类型,通常是指向派生类的指针或引用。

  • 表达式:需要转换的表达式,可以是基类指针或引用。

特性:

  1. 运行时类型检查dynamic_cast 在运行时会检查对象的实际类型。如果转换不合法,它将返回 nullptr(对于指针转换),或者抛出 std::bad_cast 异常(对于引用转换)。

  2. 仅适用于有虚函数的类dynamic_cast 依赖于 RTTI(运行时类型信息),因此只能在含有虚函数的类上使用。

使用场景:

  1. 向下转换(派生类指针转换为基类指针): 这是最常见的情况,通常是基类指针或引用需要转换为派生类指针或引用。为了安全起见,我们可以使用 dynamic_cast

  2. 安全地进行多态类型转换: 如果你有一个多态类(具有虚函数的类),你可以使用 dynamic_cast 来确保对象的实际类型与你想要转换的类型相匹配。

示例 1:指针类型的转换
#include <iostream>

class Base {
public:
    virtual void speak() { std::cout << "Base speaks\n"; }  // 虚函数
};

class Derived : public Base {
public:
    void speak() override { std::cout << "Derived speaks\n"; }
};

int main() {
    Base* basePtr = new Derived();  // 基类指针指向派生类对象
    
    // 使用 dynamic_cast 转换为派生类指针
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 向下转换
    
    if (derivedPtr) {
        derivedPtr->speak();  // 输出 "Derived speaks"
    } else {
        std::cout << "Failed to cast to Derived.\n";
    }
    
    delete basePtr;
    return 0;
}
示例 2:引用类型的转换
#include <iostream>
#include <stdexcept>

class Base {
public:
    virtual void speak() { std::cout << "Base speaks\n"; }  // 虚函数
};

class Derived : public Base {
public:
    void speak() override { std::cout << "Derived speaks\n"; }
};

int main() {
    Base& baseRef = Derived();  // 基类引用指向派生类对象
    
    try {
        // 使用 dynamic_cast 转换为派生类引用
        Derived& derivedRef = dynamic_cast<Derived&>(baseRef);  // 向下转换
        
        derivedRef.speak();  // 输出 "Derived speaks"
    } catch (const std::bad_cast& e) {
        std::cout << "Bad cast: " << e.what() << std::endl;
    }
    
    return 0;
}
示例 3:向上转换的安全性(dynamic_caststatic_cast 的区别)

dynamic_cast 也可以用于向上转换(从派生类指针转换为基类指针)。与 static_cast 不同,dynamic_cast 会进行运行时检查。对于向上转换,它的作用不明显,因为向上转换通常是安全的,但 dynamic_cast 仍然是有效的。

#include <iostream>

class Base {
public:
    virtual void speak() { std::cout << "Base speaks\n"; }
};

class Derived : public Base {
public:
    void speak() override { std::cout << "Derived speaks\n"; }
};

int main() {
    Derived* derivedPtr = new Derived();
    
    // 向上转换:从 Derived* 转换为 Base*
    Base* basePtr = dynamic_cast<Base*>(derivedPtr);  // 向上转换安全
    
    if (basePtr) {
        basePtr->speak();  // 输出 "Derived speaks"
    }
    
    delete derivedPtr;
    return 0;
}

关键点:

  1. dynamic_cast 对指针的行为

    • 如果转换成功,返回指向目标类型的指针。

    • 如果转换失败,返回 nullptr

Base* basePtr = new Base();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 转换失败
if (!derivedPtr) {
    std::cout << "Conversion failed\n";  // 输出 "Conversion failed"
}

 2. dynamic_cast 对引用的行为

  • 如果转换成功,返回目标类型的引用。

  • 如果转换失败,会抛出 std::bad_cast 异常。

Base& baseRef = *new Base();
try {
    Derived& derivedRef = dynamic_cast<Derived&>(baseRef);  // 转换失败
} catch (const std::bad_cast& e) {
    std::cout << "Bad cast exception: " << e.what() << std::endl;  // 输出异常信息
}

注意事项:

  1. 多态性要求dynamic_cast 只有在类层次结构中的基类至少有一个虚函数时才有效。没有虚函数的类没有运行时类型信息(RTTI),因此无法进行类型检查。

  2. 效率dynamic_cast 需要运行时进行类型检查,因此它的效率相对较低,尤其是在深层次的类层次结构中,使用时需要考虑性能。

  3. static_cast 的区别

    • static_cast 在编译时进行类型转换,没有运行时检查,因此没有运行时开销,适用于你能保证转换合法的情况。

    • dynamic_cast 在运行时进行类型检查,适用于你不确定转换是否合法的情况。

  4. 不能转换非类类型dynamic_cast 只能用于类之间的指针或引用转换,不能用于非类类型(如基本数据类型、数组等)。

总结:

  • dynamic_cast 是 C++ 中用于安全类型转换的操作符,特别适用于带有虚函数的类的类型转换。

  • 它在运行时进行类型检查,可以避免不安全的转换,确保程序的安全性。

  • 主要用于多态性强的类层次结构中,帮助我们判断对象的实际类型并进行适当的转换。

10.3 einterpret_cast

einterpret_cast 是 C++ 中的另一种强制类型转换操作符,它与其他类型转换(如 static_castdynamic_cast)相比,具有不同的特点。reinterpret_cast 可以用来在不同类型之间进行低级别的位级别转换,即将某种类型的指针或引用转换为另一种不相关类型的指针或引用。

reinterpret_cast 的基本概念

reinterpret_cast 可以用来执行几乎任意的指针类型转换,不管这些类型之间是否有关联。这意味着你可以将一个指针转换为另一个完全不相关的类型。例如,将一个 int* 转换为 float*,或者将 void* 转换为任何其他类型的指针。

然而,reinterpret_cast 是一种非常危险的类型转换,因为它直接操作内存,并且没有运行时检查。因此,除非你非常清楚你正在做什么,否则应尽量避免使用 reinterpret_cast,因为它可能导致未定义行为。

语法:

reinterpret_cast<目标类型>(表达式)
  • 目标类型:你希望转换成的类型,通常是指针类型或引用类型。

  • 表达式:你要进行转换的表达式,通常是指针或引用。

reinterpret_cast 的特性:

  1. 无类型安全reinterpret_cast 不会做任何的类型检查,它直接处理底层位表示,这可能会导致未定义行为,尤其是在转换不兼容类型时。

  2. 不涉及类型层次结构:它不关心源类型和目标类型之间的继承关系。即使它们之间没有任何直接关系,也可以进行转换。

  3. 转换指针和引用类型:它通常用于指针或引用类型之间的转换,可以将任意类型的指针转换为任意其他类型的指针。

  4. 可以用于地址运算reinterpret_cast 允许进行非常底层的指针转换,甚至可以用它将一个 char* 转换为 int*,或相反,这在一些低级编程中可能有用。

示例代码:

示例 1:指针类型的转换
#include <iostream>

int main() {
    int a = 10;
    // 将 int* 转换为 char*(这通常是危险的,因为它们的内存布局不同)
    char* ptr = reinterpret_cast<char*>(&a);
    
    // 输出转换后的指针地址和值
    std::cout << "Address of 'a' as char*: " << static_cast<void*>(ptr) << std::endl;
    
    // 注意:通过 char* 来访问 int 的值通常是未定义行为
    std::cout << "Accessing int through char*: " << static_cast<int>(*ptr) << std::endl;
    
    return 0;
}

这个例子展示了如何使用 reinterpret_castint* 转换为 char*。这种类型转换虽然在编译时合法,但通常是不可取的,因为它可能会引发未定义行为,尤其是访问转换后的内存时。

示例 2:转换不同类型的指针
#include <iostream>

class A {
public:
    virtual void speak() { std::cout << "Class A speaking\n"; }
};

class B {
public:
    virtual void greet() { std::cout << "Class B greeting\n"; }
};

int main() {
    A a;
    B* b = reinterpret_cast<B*>(&a);  // 将 A* 转换为 B*,这两者没有直接关系
    
    // 此时,b 可能无法正常工作,访问会引发未定义行为
    b->greet();  // 这是未定义行为,因为 A 类没有 greet 方法
    
    return 0;
}

在这个例子中,AB 是没有任何关系的两个类,但是我们强行将 A* 转换为 B*。这将导致未定义行为,通常你不应该在没有明确知道内存布局的情况下使用这种转换。

示例 3:转换整数类型指针
#include <iostream>

int main() {
    int a = 42;
    void* ptr = reinterpret_cast<void*>(&a);  // 将 int* 转换为 void*
    
    std::cout << "Address of 'a' as void*: " << ptr << std::endl;
    
    // 将 void* 转换回 int* 并访问值
    int* intPtr = reinterpret_cast<int*>(ptr);
    std::cout << "Value of 'a' via int*: " << *intPtr << std::endl;  // 输出 42
    
    return 0;
}

这个例子展示了如何使用 reinterpret_castint* 转换为 void*,然后再转换回 int*。这在需要进行内存操作时可能是有用的。

reinterpret_cast 的危险性:

  1. 不类型安全reinterpret_cast 完全绕过了类型系统的检查,它不会进行任何内存布局的验证。例如,你可以将一个类型的指针转换为另一个完全不相关类型的指针,但访问该指针时可能会导致未定义行为。

  2. 对齐问题:某些硬件平台要求特定类型的指针具有特定的内存对齐。如果你错误地将一个指针转换为不符合其原始类型要求的类型(例如将一个 int* 转换为 char*),可能会导致程序崩溃或性能下降。

  3. 内存布局差异:不同类型的对象在内存中的布局可能不同。reinterpret_cast 会直接操作内存,可能会导致访问数据时出错,特别是在涉及不同类型的类或数据结构时。

  4. 无运行时检查:与 dynamic_cast 不同,reinterpret_cast 完全依赖于编译器,并且不进行运行时检查。转换后,如果你访问转换后的数据类型,结果完全依赖于你转换时是否正确理解内存布局。

何时使用 reinterpret_cast

  • 内存操作:在某些底层的编程中(如操作系统开发、硬件驱动、嵌入式系统等),你可能需要直接操作内存,进行类型的位级转换。这时可以使用 reinterpret_cast

  • 字节流处理:如果你正在处理原始的字节流(如网络协议解析或文件格式处理),可能需要将某种类型的指针转换为 void* 或其他类型。

总结:

  • reinterpret_cast 是 C++ 中最强大、最危险的类型转换操作符。

  • 它允许你进行几乎所有类型之间的转换,但不进行类型安全检查。

  • 使用 reinterpret_cast 时要非常小心,确保你理解底层内存布局和类型转换的后果。

  • 它通常用于低级编程,如与硬件直接交互、实现自定义内存管理、处理字节流等场景,但在大多数应用程序中不建议使用。

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

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

相关文章

rknn优化教程(一)

文章目录 1. 前述2. 优化思想2.1 实时帧率2.2 多线程处理2.2.1 排序2.2.2 批量处理2.2.3 队列 2.3 进一步优化 3. 代码 1. 前述 OK&#xff0c;铺垫了很久的rknn优化&#xff0c;终于开始写了。为什么要优化呢&#xff1f;当然是我们的使用遇到了瓶颈&#xff0c;要么使用的时…

uniapp Vue2 获取电量的独家方法:绕过官方插件限制

在使用 uniapp 进行跨平台应用开发时&#xff0c;获取设备电量信息是一个常见的需求。然而&#xff0c;uniapp 官方提供的uni.getBatteryInfo方法存在一定的局限性&#xff0c;它不仅需要下载插件&#xff0c;而且目前仅支持 Vue3&#xff0c;这让使用 Vue2 进行开发的开发者陷…

【统计方法】树模型,ensemble,bagging, boosting

决策树基础 回归树 理论上&#xff0c;决策区域可以有任何形状。• 然而&#xff0c;我们选择将预测空间划分为高维矩形或框&#xff0c;这是为了简单和易于解释结果预测模型 目标&#xff1a;将预测空间划分为矩形区域&#xff0c;最小化残差平方和&#xff08;RSS&#x…

【选配电脑】CPU核显工作机控制预算5000

【选配电脑】CPU核显工作机控制预算5000 1.背景2.配置及估价3.选配的说明 1.背景 不需要独立显卡&#xff0c;内存&#xff0c;硬盘尽量大&#xff1b; 预算控制到5000&#xff0c; 主板型号&#xff0c;电源功率支持后续添加独立显卡。 时间节点&#xff1a;2025.06.07 2.配…

Mysql 插入中文乱码

session范围 查看数据库编码&#xff1a; show variables like %char%; # MySQL 5.7 字符集强制配置 # 修复 character_set_databaselatin1 等问题 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci init_connect SET NAMES utf8mb4[client] d…

96.如何使用C#实现串口发送? C#例子

Nuget包名称 System.IO.Ports 参考代码 using System; using System.IO.Ports; using System.Threading;namespace test {class Program{static void Main(){SerialPort port new SerialPort("COM1", 9600); // 配置串口port.Open();Timer timer new Timer((_) &…

【工具使用】STM32CubeMX-FreeRTOS操作系统-信号标志、互斥锁、信号量篇

一、概述 无论是新手还是大佬&#xff0c;基于STM32单片机的开发&#xff0c;使用STM32CubeMX都是可以极大提升开发效率的&#xff0c;并且其界面化的开发&#xff0c;也大大降低了新手对STM32单片机的开发门槛。     本文主要讲述STM32芯片FreeRTOS信号标志、互斥锁和信号…

大数据Spark(六十一):Spark基于Standalone提交任务流程

文章目录 Spark基于Standalone提交任务流程 一、Standalone-Client模式 1、提交命令 2、任务执行流程 二、Standalone-Cluster模式 1、提交命令 2、任务执行流程 Spark基于Standalone提交任务流程 在Standalone模式下&#xff0c;Spark的任务提交根据Driver程序运行的位…

Android 平台RTSP/RTMP播放器SDK接入说明

一、技术背景 自2015年起&#xff0c;大牛直播SDK持续深耕音视频直播领域&#xff0c;自主研发并迭代推出跨平台 RTSP/RTMP 播放模块&#xff0c;具备如下核心优势&#xff1a; 全平台兼容&#xff1a;支持 Android/iOS/Windows/Linux 等主流系统&#xff1b; 超低延迟&#…

Nodejs工程化实践:构建高性能前后端交互系统

一、工程架构设计 1.1 现代化项目初始化 采用多包管理架构&#xff1a; mkdir content-platform && cd content-platform npm init -y npx lerna init mkdir -p {packages/client,packages/server,packages/shared} 关键模块划分&#xff1a; client/: 基于Next.js…

STM32什么是寄存器

提示&#xff1a;文章 文章目录 前言一、背景二、2.12.2 三、3.1 总结 前言 前期疑问&#xff1a; 1、什么是寄存器&#xff1f; 答&#xff1a;在4GB的地址空间中&#xff0c;512MB的block2上&#xff0c;每4个字节组成32位&#xff0c;这个32位为一个单元&#xff0c;控制&a…

第六个微信小程序:教师工具集

源于工作需要&#xff0c;下面开始。 安装及使用 | Taro 文档 vscode 代码管理 git 辅助 开发技术如上&#xff1a; 1.开始创建模板 taro4.1.1 $ taro init teachers-tools 2.用vsocde开始吧。 选择 第二个文件夹找一。 (base) PS D:\react\teachers-tools> pnpm…

记录一个用了很久的git提交到github和gitee比较方便的方法

在当前git init后&#xff0c;在隐藏的git文件夹中找到config文件 [user]name thels [remote "github"]url your github repository urlfetch refs/heads/*:refs/remotes/origin/* [remote "gitee"]url your gitee repository urlfetch refs/heads/*:…

Qt Qml模块功能及功能解析

QtQml 是 Qt 6.0 中用于声明式 UI 开发和应用程序逻辑的核心模块&#xff0c;它提供了 QML 语言的支持和运行时环境。 一、主要功能 1. QML 语言支持 QML 语法解析&#xff1a;支持 QML (Qt Meta-Object Language 或 Qt Modeling Language) 的完整语法 JavaScript 集成&…

NLP学习路线图(二十九):BERT及其变体

在自然语言处理(NLP)领域,一场静默的革命始于2017年。当谷歌研究者发表《Attention is All You Need》时,很少有人预料到其中提出的Transformer架构会彻底颠覆NLP的发展轨迹,更催生了以GPT系列为代表的语言模型风暴,重新定义了人类与机器的交互方式。 一、传统NLP的瓶颈:…

【LLM-Agent】智能体的记忆缓存设计

note 实践&#xff1a;https://modelscope-agent.readthedocs.io/zh-cn/latest/modules/memory.html 文章目录 note一、Agent的记忆实现二、相关综述三、记忆体的构建四、cursor的记忆设计1. 记忆生成提示词2. 记忆评估提示词 五、记忆相关的MCPReference 一、Agent的记忆实现…

一起学Spring AI:核心概念

人工智能概念 本节描述了 Spring AI 使用的核心概念。我们建议您仔细阅读&#xff0c;以理解 Spring AI 实现背后的思想。 模型&#xff08;Models&#xff09; 人工智能模型是设计用来处理和生成信息的算法&#xff0c;通常模仿人类的认知功能。通过从大型数据集中学习模式…

PicSharp(图片压缩工具) v1.1.6

PicSharp 一个简单、高效、灵活的跨平台桌面图像压缩应用程序。软件基于Rust实现&#xff0c;高性能低资源&#xff0c;能快速扫描文件或目录&#xff0c;批处理图像。软件还具备组合压缩策略&#xff0c;TinyPNG提供最佳压缩比&#xff0c;但需要互联网连接&#xff0c;对大量…

前端文件下载常用方式详解

在前端开发中&#xff0c;实现文件下载是常见的需求。根据不同的场景&#xff0c;我们可以选择不同的方法来实现文件流的下载。本文介绍三种常用的文件下载方式&#xff1a; 使用 axios 发送 JSON 请求下载文件流使用 axios 发送 FormData 请求下载文件流使用原生 form 表单提…

【DAY42】Grad-CAM与Hook函数

内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点: 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业&#xff1a;理解下今天的代码即可 在深度学习中&#xff0c;我们经常需要查看或修改模型中间层的输出或梯度。然而&#xff0c;标准的前向传播和反…