【C++类型转换】static_cast、dynamic_cast、const_cast、reinterpret_cast

文章目录

  • [一、C++ 类型转换的背景](#一、C++ 类型转换的背景)
  • [二、C++ 类型转换详解](#二、C++ 类型转换详解)
    • [▶ static_cast 静态类型转换](#▶ static_cast 静态类型转换)
    • [▶ dynamic_cast 动态类型转换](#▶ dynamic_cast 动态类型转换)
    • [▶ const_cast 去除或添加 const/volatile](#▶ const_cast 去除或添加 const/volatile)
      • 基本用法
      • 实例
      • [添加 const/volatile(不常用)](#添加 const/volatile(不常用))
    • [▶ reinterpret_cast 低级重解释转换](#▶ reinterpret_cast 低级重解释转换)
      • 基本用法
      • 实例
      • [uintptr_t 系统无符号最大整型](#uintptr_t 系统无符号最大整型)

一、C++ 类型转换的背景

在 C 语言中,类型转换通常使用 (type) 的语法进行强制转换(C-style cast),例如:

cpp 复制代码
int a = 10;
double b = (double)a; // C 风格转换

这种方式虽然简洁,但存在以下问题:

  • 安全性差:编译器不会检查转换是否合理;
  • 可读性弱:无法从代码中一眼看出转换的意图;
  • 维护困难:全局搜索 (int) 会匹配大量无关内容。

为了解决这些问题,C++ 引入了四种更安全、更明确的强制类型转换操作符 ,它们都出现在 C++98 标准 中,并在后续标准(C++11/14/17/20)中保持稳定。这四种转换分别是:static_cast、、dynamic_castconst_castreinterpret_cast。这四大类型是 C++ 对 C 风格强制转换的现代化、安全化替代方案。

二、C++ 类型转换详解

▶ static_cast 静态类型转换

基本用法

  • 作用: 用于 编译期可确定的、相对安全的类型转换 ,包括:
    • 基本类型之间的转换(如 intdouble)。
    • 指针/引用在 有继承关系的类之间 向上或向下转换(但不安全!)
    • void*与其他指针类型之间的转换。
    • 显式调用构造函数或转换函数 (如 const char* →std::string,可直接将 char * 、const char * 类型的对象赋值给 string 对象)
    • enum 与整型之间
    • void*与其他指针类型之间(但不能用于函数指针)。
  • ⚠️ 注意: static_cast 不进行运行时类型检查,向下转型(基类 → 派生类)可能不安全!
    比如:内存里实际只分配了 Base 基类 的大小(比如 8 字节),但 Derived 派生类可能更大(比如 16 字节,因为它有额外成员);现在你拿着一个 Derived* 指针,以为它后面有 16 字节数据,其实只有 8 字节是合法的。如果你访问 Derived 特有的成员(比如 d_ptr->extraData),就会读到垃圾内存,甚至程序崩溃!
  • 样例:
cpp 复制代码
double d = 3.14;
int i = static_cast<int>(d);

实例

cpp 复制代码
#include <iostream>
using namespace std;

class Base {
    public: 
        virtual ~Base() {};
};
class Derived : public Base {};

int main() {
    // 1. 基本类型转换
    double d = 3.14;
    int i = static_cast<int>(d); // i = 3

    // 2. void* 转换
    void* p = &i;
    int* ip = static_cast<int*>(p);

    // 3. 向上转型(派生类指针->基类指针),安全
    Derived d_obj;
    Base* b1 = static_cast<Base*>(&d_obj); // OK

    // 4. 向下转型(基类指针->派生类指针)危险!无运行时检查
    Base* b2 = new Base();
    Derived* d_ptr = static_cast<Derived*>(b2); // 编译通过,但运行时行为未定义!
    delete b2;
    
    return 0;
}

▶ dynamic_cast 动态类型转换

基本用法

  • 作用: 专用于 多态类型之间的安全向下转型(基类->派生类) ,它会在 运行时检查对象的真实类型,故运行时安全! ,若转换失败:
    • 对指针:返回 nullptr
    • 对引用:抛出 std::bad_cast 异常
  • ⚠️基类必须包含至少一个虚函数,即具有虚表。
  • 应用场景:需要安全地将基类指针/引用转为派生类,且基类是多态的。
  • 样例:
cpp 复制代码
// 1. 安全向下转型,基类指针(本身所指向的就是派生类对象)→派生类指针
Base* b1 = new Derived(); // 基类指针指向派生类对象
Derived* d1 = dynamic_cast<Derived*>(b1); // 转换成功

// 2. 非安全向下转型! 基类指针(本身所指向的就是基类对象)→派生类指针
Base* b2 = new Base();
Derived* d2 = dynamic_cast<Derived*>(b2); // 转换失败,dynamic_cast 返回 nullptr


// 3. 安全向下转型,基类引用(基类引用的是派生类对象)→派生类引用
Derived derived_obj;
Base& ref1 = derived_obj; // 向上转型
Derived& r1 = dynamic_cast<Derived&>(ref1);// 转换成功

// 4. 非安全向下转型! 基类引用(基类引用的是基类对象)→派生类引用
Base base_obj;
Base& ref2 = base_obj;
Derived& r2 = dynamic_cast<Derived&>(ref2); // 转换失败,运行时会抛出 bad_cast!

实例

cpp 复制代码
#include <iostream>
#include <typeinfo>
using namespace std;

class Base {
public:
    virtual ~Base() {}  // 必须是多态类型(有虚函数)
};

class Derived : public Base {
public:
    void special() {
        cout << "Special!\n";
    }
};

int main() {
    // ========== 指针版本==========
    cout << "=== 指针版本 ===" << endl;
    Base* b1 = new Derived(); // 基类指针指向派生类对象
    Base* b2 = new Base();

    // 安全向下转型,基类指针(本身所指向的就是派生类对象)→派生类指针
    Derived* d1 = dynamic_cast<Derived*>(b1); // 转换成功
    if (d1) {
        d1->special();
    }

    // 非安全向下转型! 基类指针(本身所指向的就是基类对象)→派生类指针
    Derived* d2 = dynamic_cast<Derived*>(b2); // 转换失败,dynamic_cast 返回 nullptr
    if (!d2) {
        cout << "Pointer cast failed!" << endl; 
    }

    delete b1;
    delete b2;

    // ========== 引用版本==========
    cout << "\n=== 引用版本 ===" << endl;

    // 1. 安全向下转型,基类引用(基类引用的是派生类对象)→派生类引用
    Derived derived_obj;
    Base& ref1 = derived_obj; // 向上转型

    try {
        Derived& r1 = dynamic_cast<Derived&>(ref1); // 转换成功
        r1.special(); // 成功调用
    } catch (const bad_cast& e) {
        cout << "Reference cast failed: " << e.what() << endl;
    }

    // 2. 非安全向下转型! 基类引用(基类引用的是基类对象)→派生类引用
    Base base_obj;
    Base& ref2 = base_obj;

    try {
        Derived& r2 = dynamic_cast<Derived&>(ref2); // 转换失败,运行时会抛出 bad_cast!
        r2.special(); // 不会执行到这里
    } catch (const bad_cast& e) {
        cout << "Reference cast failed: " << e.what() << endl; // 会执行这里
    }

    return 0;
}

▶ const_cast 去除或添加 const/volatile

基本用法

  • 作用: 唯一能修改constvolatile 属性的转换操作符。 常用于:
    • 去除 const 以调用非 const 成员函数(谨慎!)
    • 与 C 接口交互(C 函数参数常为非 const)
  • ⚠️const_cast 只能用于指针、引用或成员指针类型,不能改变对象的实际类型(如 int* → double*)。
  • ⚠️ 若原对象本身是 const(如字面量、全局 const 变量),去除 const 后修改会导致 未定义行为(UB)!

实例

cpp 复制代码
#include <iostream>
using namespace std;

void legacy_func(int* p) { // C 风格函数,参数非 const
    *p = 200;
}

int main() {
    // const int x = 42;
    // int* px = const_cast<int*>(&x); // ❌ 危险!x 是真正的 const
    // *px = 99; // UB! 程序可能崩溃

    // ✅️ 正确用法:去除指针的const
    int y = 50;
    const int& cy = y; //引用的 const 性质会传递给它的地址类型: cy 是 const int&,所以 &cy 的类型是 const int*。
    int* py = const_cast<int*>(&cy);*py = 100; // 去除 &cy 的const
    cout << "y = " << y << endl; // 输出: y = 100
    legacy_func(const_cast<int*>(&cy)); // 与 C 接口兼容
    cout << "y = " << y << endl; // 输出: y = 200

    // ✅️ 正确用法:去除引用的const
    int &refy=const_cast<int &>(cy);refy=300; // 去除常引用对象的const
    cout << "y = " << y << endl; // 输出: y = 300

    return 0;
}

添加 const/volatile(不常用)

  • 虽然大家更常听说 const_cast 用于"去掉 const",但 C++ 标准明确允许它双向操作:添加 const 或 volatile。例如:
cpp 复制代码
int x = 42;
const int* p = const_cast<const int*>(&x); // 添加 const
// 等价于:const int* p = &x; (通常不需要 const_cast)

▶ reinterpret_cast 低级重解释转换

基本用法

  • 作用:
    直接操作内存中的二进制表示,不进行任何类型安全检查或数据转换。可以转换:
    • 任意指针类型之间(包括函数指针、成员指针)
    • 指针 ↔ 整数(如 int*char*
    • 不同类型的引用(极危险)
  • 不能转换的类型:基本类型之间 (如 int → double,应使用 static_cast)。

实例

cpp 复制代码
#include <iostream>
#include <cstdint>
using namespace std;

int main() {
    int a = 0x12345678;
    int* pa = &a;

    // 1. 指针转整数
    uintptr_t addr = reinterpret_cast<uintptr_t>(pa);
    cout << "Address: 0x" << hex << addr << endl;

    // 2. 不同类型指针转换(用于内存查看)
    char* pc = reinterpret_cast<char*>(pa); // int* → char*
    cout << "First byte: 0x" << (int)(unsigned char)pc[0] << endl;

    // 3. 函数指针转换(高级用法,慎用)
    void (*func)() = []{ cout << "Lambda called\n"; };
    void (*raw_func)() = reinterpret_cast<void(*)()>(func);
    raw_func(); // 可能工作,但不保证跨平台

    return 0;
}

uintptr_t 系统无符号最大整型

  • uintptr_t(u → unsigned 无符号、int → integer 整数、ptr → pointer 指针、_t → type 类型):表示 作用于指针的无符号整数类型,其设计的初衷是 足够的大,能够无损地存储任意指针的值,而且 能够最大存储的数值与系统的寻址能力有关:
平台 指针大小 sizeof(uintptr_t) 典型定义
32 位 4 字节 4 typedef uint32_t uintptr_t;
64 位 8 字节 8 typedef uint64_t uintptr_t;
  • 它定义在头文件 #include <cstdint>(C++11)#include <stdint.h>(C99)中。
相关推荐
再难也得平1 小时前
[LeetCode刷题]49.字母异位词分组(通俗易懂的java题解)
java·开发语言·leetcode
黎雁·泠崖1 小时前
Java 时间类(中):JDK8 全新时间 API 详细教程
java·开发语言
Trouvaille ~2 小时前
【Linux】epoll 深度剖析:高性能 IO 多路复用的终极方案
linux·运维·服务器·c++·epoll·多路复用·io模型
kong79069282 小时前
Python核心语法-Matplotlib简介
开发语言·python·matplotlib
mjhcsp2 小时前
C++数位 DP解析
开发语言·c++·动态规划
Coder_Boy_2 小时前
Java高级_资深_架构岗 核心知识点——高并发模块(底层+实践+最佳实践)
java·开发语言·人工智能·spring boot·分布式·微服务·架构
小龙报2 小时前
【算法通关指南:数据结构与算法篇】二叉树相关算法题:1.二叉树深度 2.求先序排列
c语言·开发语言·数据结构·c++·算法·贪心算法·动态规划
yy.y--2 小时前
Java线程实现浏览器实时时钟
java·linux·开发语言·前端·python
仰泳的熊猫2 小时前
题目1529:蓝桥杯算法提高VIP-摆花
数据结构·c++·算法·蓝桥杯