十(1). 强制类型转换

继第十部分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;  // 输出 42return 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.pswp.cn/bicheng/84034.shtml

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

相关文章

Git版本控制工具详解

如何区分开发环境和生产环境呢 答案就是写不同的配置文件&#xff0c;开发的设置成开发需要的&#xff0c;生产的设置成生产需要的&#xff0c;共同放到config这个配置文件夹下面&#xff0c;开发和生成的时候分别加载不同的配置文件 方式二就是使用相同的一个入口配置文件&a…

反向传播的核心是什么:计算损失函数对可训练参数的梯度=== 损失函数能通过计算图连接到可训练参数

反向传播的核心是什么:计算损失函数对可训练参数的梯度 损失函数能通过计算图连接到可训练参数 在深度学习中,反向传播的核心是计算损失函数对可训练参数的梯度,从而更新这些参数。对于LLM(大型语言模型)而言,是否需要“LLM输出的参数”才能进行反向传播 一、反向传播…

KINGCMS被入侵

现象会强制跳转到 一个异常网站,请掉截图代码. 代码中包含经过混淆处理的JavaScript&#xff0c;它使用了一种技术来隐藏其真实功能。代码中使用了eval函数来执行动态生成的代码&#xff0c;这是一种常见的技术&#xff0c;恶意脚本经常使用它来隐藏其真实目的。 这段脚本会检…

深入探索串的高级操作:从算法到 LeetCode 实战

串是编程中最常用的数据结构之一&#xff0c;从简单的文本处理到复杂的文本匹配算法&#xff0c;串的应用无处不在。在掌握了串的基本概念、存储结构以及KMP算法之后&#xff0c;现在让我们深入探索串的更多高级操作&#xff0c;例如求子串、串的替换等&#xff0c;并通过LeetC…

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …

OneNet + openssl + MTLL

1.OneNet 使用的教程 1.在网络上搜索onenet&#xff0c;注册并且登录账号。 2.产品服务-----物联网服务平台立即体验 3.在底下找到立即体验进去 4.产品开发------创建产品 5.关键是选择MQTT&#xff0c;其他的内容自己填写 6.这里产品以及开发完成&#xff0c;接下来就是添加设…

【Fiddler工具判断前后端Bug】

Fiddler工具判断前后端Bug的方法 使用Fiddler抓包工具可以高效定位问题是出在前端还是后端&#xff0c;主要通过分析请求和响应的内容、状态码、数据格式等关键信息。 分析请求是否成功发送 检查请求是否从客户端正确发出&#xff0c;观察Fiddler抓取的请求列表。若请求未出…

【论文阅读笔记】《A survey on deep learning approaches for text-to-SQL》

文章目录 一、论文基本信息1. 文章标题2. 所属刊物/会议3. 发表年份4. 作者列表5. 发表单位 二、摘要三、解决问题四、创新点五、自己的见解和感想六、研究背景七、研究方法&#xff08;模型、实验数据、评估指标&#xff09;八、总结&#xff08;做了什么、得到了什么、有什么…

【强连通分量 缩点 最长路 拓扑排序】P2656 采蘑菇|普及+

本文涉及知识点 C图论 强连通分量 缩点 最长路 拓扑排序 P2656 采蘑菇 题目描述 小胖和 ZYR 要去 ESQMS 森林采蘑菇。 ESQMS 森林间有 N N N 个小树丛&#xff0c; M M M 条小径&#xff0c;每条小径都是单向的&#xff0c;连接两个小树丛&#xff0c;上面都有一定数量的…

Dubbo Logback 远程调用携带traceid

背景 A项目有调用B项目的服务&#xff0c;A项目使用 logback 且有 MDC 方式做 traceid&#xff0c;调用B项目的时候&#xff0c;traceid 没传递过期&#xff0c;导致有时候不好排查问题和链路追踪 准备工作 因为使用的是 alibaba 的 dubbo 所以需要加入单独的包 <depend…

nodejs:用 nodemailer 发送一封带有附件的邮件

我们将使用 nodemailer 库来发送带有附件的邮件。 首先&#xff0c;确保已经安装了nodemailer。如果没有安装&#xff0c;可以通过 npm install nodemailer 来安装。 cnpm install nodemailer --save dependencies: – nodemailer ^7.0.3 步骤&#xff1a; 引入nodemailer模…

Scade 语言概念 - 方程(equation)

在 Scade 6 程序中自定义算子(Operator)的定义、或数据流定义(data_def)的内容中&#xff0c;包含一种基本的语言结构&#xff1a;方程(equation)(注1)。在本篇中&#xff0c;将叙述 Scade 语言方程的文法形式&#xff0c;以及作用。 注1: 对 Scade 中的 equation, 或 equation…

STM32开发,创建线程栈空间大小判断

1. 使用RTOS提供的API函数&#xff08;以FreeRTOS为例&#xff09; 函数原型&#xff1a;UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)功能&#xff1a;获取指定任务堆栈中剩余的最小空间&#xff08;以字为单位&#xff0c;非字节&#xff09;。使用步骤&am…

thinkphp8.1 调用巨量广告API接口,刷新token

1、在mysql中建立表sys_token; CREATE TABLE sys_token (id int UNSIGNED NOT NULL,access_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,expires_in datetime NOT NULL,refresh_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,refresh_token_expires_in …

【leetcode】递归,回溯思想 + 巧妙解法-解决“N皇后”,以及“解数独”题目

&#x1f4da;️前言 &#x1f31f; 本期内容亮点&#xff1a;我们将深入解析力扣&#xff08;LeetCode&#xff09;上的几道经典算法题&#xff0c;涵盖不同难度和题型&#xff0c;帮助大家掌握解题思路和代码实现技巧。无论是准备面试还是提升算法能力&#xff0c;这些题解都…

【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越狱教程

前言 越狱iPhone之后&#xff0c;一定记得安装一下用于屏蔽更新的描述文件&#xff08;可使用爱思助手&#xff09; 因为即便关闭了自动更新&#xff0c;iPhone仍会在某些时候自动更新系统&#xff0c;导致越狱失效&#xff1b;更为严重的是&#xff0c;更新后的iOS版本可能是…

​​高频通信与航天电子的材料革命:猎板PCB高端压合基材技术解析​​

—聚酰亚胺/陶瓷基板在5G与航天场景的产业化应用​​ ​​一、极端环境材料体系&#xff1a;突破温域与频率极限​​ ​​聚酰亚胺基板&#xff08;PI&#xff09;的航天级稳定性​​ 猎板在卫星通信PCB中采用真空层压工艺处理聚酰亚胺基材&#xff08;Dk≈10.2&#xff09;&a…

pikachu靶场通关笔记13 XSS关卡09-XSS之href输出

目录 一、href 1、常见取值类型 2、使用示例 3、安全风险 二、源码分析 1、进入靶场 2、代码审计 3、渗透思路 三、渗透实战 1、注入payload1 2、注入payload2 3、注入payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff…

day26-计算机网络-4

1. tcp的11种状态 ss -ant -a 表示看所有状态 -n 表示不将ip解析为主机名 -t 表示tcp 1.1. closed状态&#xff08;客户端、服务端&#xff09; 客户端发起建立连接前的状态服务端启动服务前的状态 1.2. listen状态&#xff08;服务端&#xff09; 服务端软件运行的时候状…

基于autodl部署Cross-Modal-Re-ID-baseline

https://arxiv.org/abs/2001.04193 https://github.com/mangye16/Cross-Modal-Re-ID-baseline/tree/master?tabreadme-ov-file# 需要SYSU-MM01.zip pip install numpy pandas scipy scikit-learn pillow tqdm把SYSU-MM01放到…/Datasets/SYSU-MM01/ori_data下 先运行pytho…