C/C++中类型转换:static_cast, dynamic_cast, const_cast, reinterpret_cast

2024-01-05 02:52

本文主要是介绍C/C++中类型转换:static_cast, dynamic_cast, const_cast, reinterpret_cast,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 目的
    • 定义和作用

目的

在C中一般是使用(int) x, (int *)x, (void *) p等方式,但是这种强制类型转换的方式是不安全的,因为他没有类型检查。而且这种转换也是不够精确的。
因此在C++中引入了几种强制类型转换的方式。

定义和作用

  1. static_cast:

    1. 提供安全的基本类型转换:但是要注意数据的截断或者改变。(float转换为整数的时候丢失小数部分)。
    2. 类层次间转换:
      向上转换(将派生类的指针/引用转换为基类的指针/引用),这是安全的,因为派生类总是包含基类的全部信息。
      向下转换(将基类的指针/引用转换为派生类的指针/引用),这是不安全的,因为基类不一定包含派生类的全部信息。
    3. 空指针转换为目标类型的空指针:
      class Base {};
      class Derived : public Base {};Base* b = nullptr;
      Derived* d = static_cast<Derived*>(b);  // 安全转换,d 仍然是 nullptr。
      
    4. non_const转换为const
      int x = 10;
      const int* px = static_cast<const int*>(&x);
      
  2. dynamic_cast:
    用于多态,尤其是向下转换和侧向转换(在同一层次结构中的不同类之间)。
    在执行转换时进行运行时检查。如果是安全的(即对象确实是目标类型或其派生类型的实例),则转换成功;如果不安全则转换失败(对于指针,返回nullptr,对于引用抛出异常。

    class Base { virtual void dummy() {} };
    class Derived : public Base { /* ... */ };Base* basePtr = new Derived();// 安全的向下转换
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    

    注意:

    1. 使用dynamin_cast必要要求设计的类至少有一个虚函数,因为涉及多态。
    2. 编译时无法确定转换结果,所以是运行时检查。

    补充侧向转换:
    指的是在同一继承层次中,将一个基类的指针或引用转换为另一个并行基类的指针或引用。
    这种转换通常发生在多重继承的情况下,当一个派生类继承自多个基类时。
    侧向转换的场景
    假设有一个多重继承的类层次结构,其中 Derived 类继承自两个基类 Base1 和 Base2:

    Copy code
    class Base1 { /* ... */ };
    class Base2 { /* ... */ };
    class Derived : public Base1, public Base2 { /* ... */ };
    

    在这种结构中,Derived 同时是 Base1 和 Base2 的子类。如果你有一个指向 Base1 的指针,且它实际上指向的是一个 Derived 类型的对象,你可以使用侧向转换将这个指针安全地转换为指向 Base2 的指针:

    Copy code
    Derived d;
    Base1* b1 = &d;
    Base2* b2 = dynamic_cast<Base2*>(b1); // 侧向转换
    

在这个例子中,dynamic_cast 被用来从 Base1 类型转换到 Base2 类型。由于这两个类是在同一个继承层次中,这样的转换是可能的,并且如果 b1 确实指向一个 Derived 类型的对象,转换将会成功。

  1. const_cast:
    去除对象指针/引用的const属性
    const_cast 是 C++ 中的一种类型转换操作符,专门用于修改对象的 constvolatile 属性。它是用来改变对象的常量性(const-ness)或易失性(volatility)的。
    使用场景与目的

    1. 移除 constvolatile 限定:

      • 当你有一个指向常量的指针或引用,但需要调用一个非常量成员函数或需要修改其指向的数据时,可以使用 const_cast 来移除 constvolatile 限定。
      • 这常用于与旧的C风格代码交互,其中数据不是常量,但接口要求传递常量参数。
    2. 添加 const 限定:

      • 虽然不常见,但 const_cast 也可以用来给对象添加 const 限定。这在某些特定情况下可能有用。
        使用示例
    const int a = 10;
    int* b = const_cast<int*>(&a);  // 移除 const 限定
    *b = 20;  // 未定义行为,因为 a 是一个真正的常量
    

    在此例中,尽管 const_cast 成功移除了 const 限定,但对 a 的修改是未定义行为,因为 a 本身是一个常量。

    局限(缺点)

    1. 未定义行为风险:
      如果原始对象实际上是一个常量,尝试修改它的值(即使使用 const_cast 移除了 const 限定)将导致未定义行为。

    2. 安全性问题:
      滥用 const_cast 可能导致代码难以理解和维护,因为它破坏了 const 正确性,这是 C++ 用于保证对象不被意外修改的机制之一。

    3. 限制用途:
      const_cast 只能改变对象的 const/volatile 限定,不能用于改变对象的实际类型。

    4. 破坏封装性:
      在类的上下文中,const_cast 可能会破坏类的封装性,因为它允许修改本应被保护的成员。

  2. reinterpret_cast:
    它用于在不兼容类型之间进行转换,基本上是直接重新解释底层的位模式。由于这种转换的低级性质,它通常在特定的系统编程和硬件相关的场景中使用。

    使用场景与目的

    1. 不兼容类型之间的转换:
      用于在完全不相关的类型之间进行转换,例如将指针转换为足够大的整数类型,或将不同类型的指针之间进行转换。
    2. 系统级或硬件相关编程:
      在底层编程中,如操作系统内核或硬件接口编程时,可能需要将指针或其他类型直接转换为某种特定格式。
    3. 函数指针的转换:
      用于将一个类型的函数指针转换为另一个类型。这在需要将函数指针传递给期望不同签名的函数指针参数的回调函数时特别有用。
      示例
    Copy code
    int* ip = new int(42);
    char* cp = reinterpret_cast<char*>(ip);  // 将 int* 转换为 char*
    

    在此示例中,ip 指向一个整数,使用 reinterpret_cast 将其转换为指向 char 的指针。

    局限(缺点)

    1. 类型安全问题:
      reinterpret_cast 不进行任何类型安全检查。它简单地重新解释给定值的位模式,这可能导致未定义行为,特别是在不恰当地解释数据时。
    2. 可移植性问题:
      由于它依赖于特定平台的底层数据表示方式,所以使用 reinterpret_cast 的代码通常缺乏可移植性。
    3. 维护性和可读性问题:
      这种转换的意图不够明确,可能导致代码难以理解和维护。
    4. 潜在的运行时错误:
      错误的使用 reinterpret_cast 可能导致程序崩溃、数据损坏或其他难以调试的运行时错误。
  3. 违反别名规则:
    在某些情况下,reinterpret_cast 可能违反 C++ 的严格别名规则(Strict Aliasing Rule),这可能导致编译器优化产生问题。

这篇关于C/C++中类型转换:static_cast, dynamic_cast, const_cast, reinterpret_cast的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/571435

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给