C++中的四种类型转换分别为:static_cast,const_cast,reinterpret_cast,dynamic_cast。
=========================================================================
1.static_cast 静态转换
这是最常用、最通用的转换运算符,用于在"有理由"相互关联的类型之间进行转换。
static_cast 在编译时 使用类型信息执行转换,在转换执行必要的检测(如指针越界计算,类型检查等);但没有 运行时 类型检查来保证转换的安全性。
主要用途:
基本数据类型之间的转换 (例如 int 转 double, enum 转 int)。
cpp
int i = 10;
double d = static_cast<double>(i); // 将整数i转换为双精度浮点数d
2.
void 指针 与其他具体类型指针之间的转换。
cpp
void* ptr = malloc(100);
int* iptr = static_cast<int*>(ptr); // 将void*转换为int* 2.1//注意! 指针转换的限制 //不能去除掉const等特性 //具体指针类型不同不可以转换

(
volatile - 易变限定符
volatile 告诉编译器该变量可能会在程序控制之外被改变,因此编译器不应对其进行优化。
__unaligned - 非对齐限定符
用于告诉编译器该数据可能没有按照处理器的自然对齐方式存储,需要特殊处理。
注意 :__unaligned 不是标准的 C++ 关键字,而是某些编译器(主要是 Microsoft VC++)的扩展。
)
3.类层次结构中的向上转换 (Upcasting)
将派生类指针或引用转换为基类指针或引用。这是安全的,编译器会自动进行,但显式使用 static_cast 也可以。
-
cpp
class Base {};
class Derived : public Base {};
Derived d;
Base* b = static_cast<Base*>(&d); // 安全,向上转换
//类层次结构中的向下转换 (Downcasting):将基类指针或引用转换为派生类指针或引用。这是不安全的 ,因为编译器无法在编译时确定指针所指的实际对象类型。如果使用 static_cast 进行向下转换,需要程序员自己确保转换是安全的。如果不安全,会导致未定义行为。
-
cpp
-
Base* b = new Derived; // 实际上指向一个Derived对象
-
Derived* d = static_cast<Derived*>(b); // 编译通过,但风险由程序员承担
4.显示的弃值表达式
弃值表达式 是指在 C++ 中表达式被求值,但其结果被有意丢弃而不使用的表达式。
cpp
int main()
{
int a = 10, b = 20;
static_cast<void>(a+b); // 这就是一个弃值表达式
}
第4行 static_cast<void>(a+b) 就是一个典型的弃值表达式:
-
计算
a + b得到结果 30 -
使用 static_cast<void> 明确表示我们故意要丢弃这个值
-
整个表达式的结果被丢弃,不赋值给任何变量,也不用于任何计算
5.左值到右值的转换
cpp
int main()
{
int a=10;
int &x=a;
int && rx=a;//erro
int && rx=static_cast<int&&>(a);
}
特点:
-
转换在编译期完成。
-
不能移除
const或volatile属性(这是const_cast的工作)。
=========================================================================
2.const_cast 常量转换
这是唯一一个有能力移除或添加 对象的 const(常量)或 volatile(易变)属性的转换运算符。
主要用途:
-
移除 const 属性 :用于修改一个原本不是 const (指针,引用等等)但被声明为 const (常量指针,常量引用等等)的变量。
cpp
void print(char* str) { // 一个不接受const指针的函数
std::cout << str << std::endl;
}
const char* greeting = "Hello, World!";
// print(greeting); // 错误!不能将const char*传递给char*
print(const_cast<char*>(greeting)); // 正确,移除了const
重要警告 :如果原始对象本身就是一个常量(字符串常量等等),通过
const_cast修改它是未定义行为。cpp
const int ci = 10;
int* modifiable = const_cast<int*>(&ci);
*modifiable = 20; // 未定义行为!可能导致程序崩溃或意外结果
特点:
特点:
-
不能用于在不同类型之间进行转换(例如
int转double),只能修改类型的 const/volatile 属性。 -
常量指针被转化为非常量指针,仍然指向原来的对象
-
常量引用被转换为非常量引用,仍然引用原来的对象======================================================================
3.reinterpret_cast 重新解释转换这是最危险 的转换,它提供了底层的、基于比特位的重新解释。它仅仅将一块内存的二进制数据当作另一种完全不同的类型来处理,不进行任何类型检查。
主要用途:
-
在不相关类型的指针之间转换 (例如
int*转double*)。 -
在指针和整型之间转换。
cpp
int* p = new int(65); char* ch = reinterpret_cast<char*>(p); // 将int*强行解释为char* std::cout << *ch << std::endl; // 输出 'A',因为65是'A'的ASCII码 // 将指针转换为一个足够大的整数(例如uintptr_t) uintptr_t addr = reinterpret_cast<uintptr_t>(p); -
转换结果几乎总是依赖于编译器实现,不可移植。
-
极其危险,必须非常清楚自己在做什么,并且确保转换是合乎逻辑的。
-
通常用于底层编程,如驱动、序列化、网络协议等场景
=========================================================================
4. dynamic_cast 动态转换
专门用于处理多态 (涉及虚函数)的类层次结构的安全向下转换。
主要用途:
-
安全的向下转换 :将基类指针或引用安全地转换为派生类指针或引用,具有类型检查,比static_cast 更安全。//在进行上行转换时,dynamic_cast 和static_cast 的效果是一样的。
-
交叉转换 (Cross Casting):在多重继承中,将一个基类指针转换为另一个不直接相关的基类指针。
-----对于 上行转换 共有继承,无虚函数(无虚表,无RTTI) 的情况,为静态转换,dynamic_cast 和static_cast 的效果一样。


-----对于 下行转换情况,需公有继承,至少有一个虚函数。
执行过程是 先找到对象,再找虚表,再判断RTII //动态转换
如果没有虚函数,则在编译时则停止
//如 Base 有两个派生类 Derived 和 AnotherDerived:
当进行向下转换时,如果Base 已经指向了某个派生类 如 Base* basePtr1 = new Derived();(即提前进行过向上转换),则再进行向下转换时,Base只能被成功转换为和之前指向的派生类相同类型的派生类 如 Derived,而不能被转换为其他的派生类 如 AnotherDerived。
如果转换失败,static_cast会导致未定义行为,而dynamic_cast会返回空,或抛出异常。
(这也是两者之间的区别)
cpp
class Base {
public:
virtual ~Base() {}
virtual void foo() { cout << "Base::foo" << endl; }
};
class Derived : public Base {
public:
void foo() override { cout << "Derived::foo" << endl; }
void derivedMethod() { cout << "Derived特有方法" << endl; }
};
class AnotherDerived : public Base {
public:
void foo() override { cout << "AnotherDerived::foo" << endl; }
};
场景分析
场景1:正确的转换
cpp
Base* basePtr = new Derived(); // 实际指向Derived对象
Derived* derivedPtr = static_cast<Derived*>(basePtr); // ✅ 安全
derivedPtr->derivedMethod(); // ✅ 正常工作
发生了什么:
-
static_cast在编译时直接进行指针偏移计算 -
由于实际对象确实是
Derived类型,转换是正确的 -
虚表指针保持不变,虚函数调用正常
场景2:错误的转换(危险!)
cpp
Base* basePtr = new AnotherDerived(); // 实际指向AnotherDerived
Derived* derivedPtr = static_cast<Derived*>(basePtr); // ❌ 危险!
derivedPtr->derivedMethod(); // ❌ 未定义行为!
会发生什么:
-
static_cast仍然会进行指针偏移计算 -
但实际对象是
AnotherDerived,没有derivedMethod -
调用
derivedMethod()会导致未定义行为:-
可能崩溃
-
可能执行错误代码
-
可能破坏内存
-
技术实现差异
| 特性 | static_cast |
dynamic_cast |
|---|---|---|
| 检查时机 | 编译时 | 运行时 |
| 检查机制 | 无类型检查 | 通过RTTI和虚表检查 |
| 失败行为 | 未定义行为 | 返回nullptr(指针)或抛异常(引用) |
| 性能 | 零运行时开销 | 有运行时开销 |
| 安全性 | 不安全 | 安全 |
| 适用场景 | 确定类型正确的场景 | 不确定类型,需要安全检查的场景 |
工作机制:
它在运行时 利用 RTTI 来检查转换的有效性。
-
对于指针 类型:如果转换成功,返回目标类型的指针;如果失败(例如,基类指针并不指向目标派生类对象),则返回 nullptr。
cpp
class Base { public: virtual ~Base() {} }; // 必须有虚函数 class Derived : public Base {}; Base* b1 = new Derived; // 实际指向Derived Base* b2 = new Base; // 实际指向Base Derived* d1 = dynamic_cast<Derived*>(b1); // 成功,d1指向有效的Derived对象 Derived* d2 = dynamic_cast<Derived*>(b2); // 失败,d2是nullptr if(d2) { // 安全的做法:检查返回值 // ... 使用d2 } else { std::cout << "转换失败!" << std::endl; } -
对于引用 类型:如果转换失败,它会抛出一个 std::bad_cast 异常,因为引用不能为
null。cpp
try { Derived& rd = dynamic_cast<Derived&>(*b2); // 转换失败,抛出异常 } catch (const std::bad_cast& e) { std::cerr << "转换失败: " << e.what() << std::endl; }
特点:
-
是唯一 在运行时进行类型检查的转换运算符。
-
有运行时开销。
-
要求基类至少有一个虚函数(这样才能有 RTTI 信息)。
(
RTTI(运行时类型识别)详解
定义:RTTI(Run-Time Type Identification)是一种机制,允许程序在运行时:
-
确定指针或引用所指向对象的实际类型
-
安全地进行类型转换
-
获取类型的详细信息
核心思想:在面向对象编程中,通过基类指针或引用操作对象时,有时需要知道对象的具体派生类型。
RTTI 的两个核心操作符
- typeid 操作符
功能 :获取表达式的类型信息,返回一个 std::type_info 对象的引用。
typeid 的重要特性:
-
对于多态类型(有虚函数的类),返回动态类型(实际对象类型)
-
对于非多态类型,返回静态类型(声明时的类型)
-
可以用于类型比较和获取类型名称
2. dynamic_cast 操作符
功能:在类层次结构中安全地进行向下转换(downcast)或交叉转换(crosscast)。
dynamic_cast 的重要特性:
| 转换类型 | 指针转换 | 引用转换 |
|---|---|---|
| 成功 | 返回目标类型指针 | 返回目标类型引用 |
| 失败 | 返回 nullptr |
抛出 std::bad_cast 异常 |
)