C++四种类型转换cast及其底层实现

这四种转换操作符是:
static_cast
dynamic_cast
const_cast
reinterpret_cast

  1. static_cast- 静态转换
    这是最常用、最通用的转换操作符,用于在编译期已知的、有某种关联的类型之间进行转换。
    主要用途:
    基本数据类型之间的转换:如 int转 double, enum转 int等。
    上行转换:将派生类指针或引用转换为基类指针或引用。这是安全的。
    下行转换:将基类指针或引用转换为派生类指针或引用。编译器不会进行运行时检查,因此不安全。​ 如果基类指针实际指向的不是目标派生类对象,使用转换后的指针会导致未定义行为。
    空指针转换:如将 void*转换为具体类型的指针。
    显式调用构造函数或转换函数。
    语法:
new_type var = static_cast<new_type>(expression);

示例:

int i = 10;
double d = static_cast<double>(i); // int -> double

class Base {};
class Derived : public Base {};

Base* base_ptr = new Derived();
// 上行转换:安全
Derived* derived_ptr = static_cast<Derived*>(base_ptr);

Base* base_ptr2 = new Base();
// 下行转换:不安全!base_ptr2 并不指向一个 Derived 对象
Derived* dangerous_ptr = static_cast<Derived*>(base_ptr2); // 危险!

底层实现:
static_cast的“转换”动作通常发生在编译时期。
对于基本数据类型(如 int到 double),编译器会生成相应的机器指令来重新解释内存中的位模式(例如,将整数格式转换为IEEE 754浮点数格式)。
对于类指针的上行/下行转换,在单继承情况下,指针的值通常不需要改变。因为派生类对象的开头就是基类子对象,所以 Derived和 Base指向的是同一个内存地址。转换只是一个编译期的类型检查过程。
在多继承情况下,上行转换(指向第二个或之后的基类)可能需要调整指针的值,因为第二个基类子对象位于派生类对象内存布局的偏移位置。下行转换时则需要反向调整。这个偏移量是编译期已知的,所以 static_cast会在编译时计算并嵌入调整指令。
核心思想:​ 在编译期计算好所有必要的地址偏移量(如果需要的话),并生成相应的指令。

  1. dynamic_cast- 动态转换
    专门用于处理继承层次结构中,具有多态性(即含有虚函数)的类的指针或引用的安全下行转换和交叉转换。
    主要用途:
    安全的下行转换和交叉转换:它会在运行时检查转换是否有效。如果转换失败:
    对于指针类型,返回 nullptr。
    对于引用类型,抛出 std::bad_cast异常。
    限制:
    只能用于含有虚函数的类(多态类型)。
    需要运行时类型信息(RTTI)的支持,这可能会带来轻微的性能开销。

语法:

// 用于指针
new_type* ptr = dynamic_cast<new_type*>(expression);
// 用于引用
new_type& ref = dynamic_cast<new_type&>(expression);

示例:

class Base { virtual void dummy() {} }; // 必须有虚函数
class Derived : public Base {};

Base* base_ptr = new Derived();

// 安全的下行转换
Derived* derived_ptr = dynamic_cast<Derived*>(base_ptr);
if (derived_ptr != nullptr) {
    // 转换成功,安全使用 derived_ptr
} else {
    // 转换失败
}

Base* base_ptr2 = new Base();
Derived* failed_ptr = dynamic_cast<Derived*>(base_ptr2);
// failed_ptr 将是 nullptr

底层实现(RTTI 和 vptr/vtable):
dynamic_cast的运行时检查依赖于 RTTI。其实现通常与虚函数表(vtable)机制紧密相关:
vptr 和 vtable:任何一个包含虚函数的类的对象,在其内存布局中都有一个隐藏的成员——虚表指针(vptr),它指向一个虚函数表(vtable)。
RTTI 信息存储:编译器会在 vtable 的某个固定位置(通常是负偏移处)存储一个 type_info对象(或类似结构),该对象包含了类的完整类型信息。
运行时检查过程:
当执行 dynamic_cast<Derived*>(base_ptr)时,代码会通过 base_ptr找到对象的 vptr。
通过 vptr 找到 type_info对象。
检查 Derived的类型是否与当前对象的类型相同,或者是其基类(对于上行转换,dynamic_cast也可以工作,但通常用 static_cast)。
为了处理复杂的继承关系(如多重继承、菱形继承),这个过程可能涉及遍历整个继承树。如果转换是合法的,dynamic_cast会计算出正确的指针偏移量(如果需要)并返回调整后的指针;否则返回 nullptr。
性能开销:​ 由于需要在运行时查询类型信息并可能遍历继承树,dynamic_cast比 static_cast慢得多。

  1. const_cast- 常量性转换
    这是唯一能够移除或增加 const(和 volatile)属性的转换操作符。
    主要用途:
    移除 const属性,以便修改原本被声明为 const的对象。
    重要警告:如果原始对象本身就是一个常量(例如用 const int x = 5;声明的),那么通过 const_cast移除其 const性并尝试修改它是未定义行为。只有在指向的对象本身不是常量的情况下(例如,一个非常量对象被一个 const指针指向了),使用 const_cast来移除 const才是安全的。
    语法:
new_type var = const_cast<new_type>(expression);

示例:

// 合法且安全的用法
int value = 10;
const int* const_ptr = &value; // 用const指针指向非常量对象
int* non_const_ptr = const_cast<int*>(const_ptr);
*non_const_ptr = 20; // OK,因为 value 本身不是 const

// 危险!未定义行为!
const int immutable_value = 30;
const int* bad_ptr = &immutable_value;
int* dangerous_ptr = const_cast<int*>(bad_ptr);
*dangerous_ptr = 40; // 未定义行为!可能导致程序崩溃或数据错误。

底层实现:
const_cast在运行时不产生任何CPU指令。它纯粹是一个编译期指令,告诉编译器:“忽略类型的 const或 volatile属性”。它不会改变指针的值,也不会改变内存中的位模式。它只是改变了编译器对那段内存的“解读方式”。

  1. reinterpret_cast- 重新解释转换
    这是最强大但也最危险的转换。它提供了低层次的、基于比特模式的重新解释。
    主要用途:
    在不相关的指针类型之间进行转换(如 Foo转 Bar)。
    在指针和足够大的整数类型之间进行转换(如 void*转 intptr_t)。
    用于函数指针类型之间的转换。
    语法:
new_type var = reinterpret_cast<new_type>(expression);

示例:

int i = 42;
// 将 int* 强行解释为 double*
double* d_ptr = reinterpret_cast<double*>(&i);
// 使用 d_ptr 是极度危险的,因为 int 和 double 的内存布局完全不同

// 指针和整数之间的转换
void* p = malloc(100);
intptr_t addr = reinterpret_cast<intptr_t>(p); // 将指针转为整数

class A {};
class B {};
A* a = new A();
// 两个完全无关的类指针之间的转换
B* b = reinterpret_cast<B*>(a); // 语法上合法,但语义上毫无意义且危险

底层实现:
与 const_cast类似,reinterpret_cast在运行时通常不产生任何指令。它只是告诉编译器:“把这块内存的比特序列,当作新的类型来处理”。它不进行任何数据对齐检查、大小检查或类型关系检查。它的结果高度依赖于平台和编译器,可移植性极差。
危险之处:​ 它完全绕过了C++的类型系统,很容易导致对齐错误、访问违例和未定义行为。

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

折戟不必沉沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值