目录
4. reinterpret_cast - 重解释转换(最底层)
在C语言中,涉及显示的类型转换时,常用(预期的转换类型)来表示,这种写法虽然看上去很方便,但是在可读性以及安全性上面的考虑都不太好。
在C++中就引入了四种用于类型转换的类型转换运算符,它们更安全、更明确。
总结表
转换运算符 | 中文名 | 主要用途 | 安全性 | 特点 |
---|---|---|---|---|
static_cast |
静态转换 | 相关类型间的转换 | 较安全 | 最常用,编译期检查 |
dynamic_cast |
动态转换 | 沿继承层级的安全向下转换 | 最安全 | 运行期检查,需要 RTTI |
const_cast |
常量转换 | 移除 const 和 volatile 属性 |
不安全 | 唯一能操作常量性的方式 |
reinterpret_cast |
重解释转换 | 低级的位模式重新解释 | 不安全 | "我确定知道我在做什么" |
1. static_cast
- 静态转换(最常用)
用途:用于相关类型之间比较"自然"和明确定义的转换。
基本数据类型转换:
int
->float
,double
->int
(会有精度损失警告)void指针 与具体类型指针之间的转换
类层次结构中:向上转换(派生类指针/引用 -> 基类指针/引用,是安全的)
编译器隐式执行的任何转换,都可以用
static_cast
显式完成
特点:转换在编译期完成,没有运行时开销。
int i = 10;
float f = static_cast<float>(i); // int -> float
void* pv = &i;
int* pi = static_cast<int*>(pv); // void* -> int*
Base* base_ptr = static_cast<Base*>(derived_ptr); // 向上转换,安全
2. dynamic_cast
- 动态转换(最安全)
用途:主要用于多态类型的安全向下转换(基类指针/引用 -> 派生类指针/引用)。
需要基类至少有一个虚函数(拥有虚表)
在运行时检查转换是否有效。如果无效:
对指针转换返回
nullptr
对引用转换抛出
std::bad_cast
异常
特点:运行期检查,有性能开销,但安全性最高。
dynamic_cast
的核心机制在于:运行时类型信息 (RTTI)
dynamic_cast
的强大之处在于它能在程序运行时检查对象的实际类型,而不仅仅是看指针的静态类型。这种能力依赖于一个叫做 RTTI (Run-Time Type Information) 的机制。
也就是为什么需要有虚函数的原因,因为 RTTI 信息就存储在对象的虚表中。
虚表不仅包含了该类所有虚函数的地址,还包含了一个至关重要的指针,指向一个
type_info
对象。
type_info
对象就是这个类的“身份证”,它包含了类的名称、继承关系等完整的类型信息。当一个包含虚函数的类被实例化时,编译器会在对象的内存布局的开头添加一个隐藏的指针,称为 虚指针 (vptr)。
这个
vptr
指向该类的vtable
。通过
vptr
->vtable
->type_info
这个链,任何一个多态对象在运行时都能准确地找到自己的类型信息。
当执行 Derived* pd = dynamic_cast<Derived*>(base_ptr);
时,编译器会在运行时生成类似如下的逻辑:
if (base_ptr != nullptr &&
base_ptr->__vptr->__type_info is compatible with Derived's type_info) {
// 计算派生类指针的偏移量(处理多继承时尤为重要)
pd = adjusted_address_of_derived;
} else {
pd = nullptr;
}
1. 指针类型的 dynamic_cast
Derived* pd = dynamic_cast<Derived*>(base_ptr);
注意:dynamic_cast
检查的是"对象实际上是什么",而不是"指针被声明为什么类型"
简单理解就是当你执行 Derived* d2 = dynamic_cast<Derived*>(base2);
时,dynamic_cast
会在运行时问一个问题:
"指针 base2
指向的那个内存中的对象,它实际上是一个 Derived
对象吗?"
示例:
#include <iostream>
#include <typeinfo> // 用于 typeid
class Base {
public:
virtual void whoami()
{
std::cout<<"i am base,address is:"<<this<<std::endl;
}
virtual ~Base() {} // 必须有至少一个虚函数
};
class Derived : public Base {
public:
virtual void whoami() override
{
std::cout<<"i am derived,address is:"<<this<<std::endl;
}
void DerFunction() { std::cout << "Derived Function.\n"; }
};
int main() {
Base* base1 = new Derived; // 基类指针实际指向派生类对象
base1->whoami();
Base* base2 = new Base; // 基类指针指向基类对象
base2->whoami();
// 尝试向下转换
Derived* d1 = dynamic_cast<Derived*>(base1);
if (d1 != nullptr) {
std::cout << "base1 conversion successful.\n";
d1->DerFunction(); // 安全调用派生类方法
} else {
std::cout << "base1 conversion failed.\n";
}
Derived* d2 = dynamic_cast<Derived*>(base2);
if (d2 != nullptr) {
std::cout << "base2 conversion successful.\n";
d2->DerFunction();
} else {
std::cout << "base2 conversion failed.\n"; // 这里会执行
}
delete base1;
delete base2;
return 0;
}
2. 引用类型的 dynamic_cast
Derived& rd = dynamic_cast<Derived&>(base_ref);
失败时会抛出 std::bad_cast
异常
try {
Derived& rd = dynamic_cast<Derived&>(*base2); // *base2 是 Base 对象
// 如果转换成功,使用 rd
} catch (const std::bad_cast& e) {
std::cerr << "Dynamic cast failed: " << e.what() << '\n'; // 这里会捕获异常
}
3. 交叉转换 (Cross Cast)
dynamic_cast
不仅仅能做简单的向下转换,还能在多重继承中完成“交叉”转换。
class Base1 { public: virtual ~Base1() {} };
class Base2 { public: virtual ~Base2() {} };
class Derived : public Base1, public Base2 {}; // 多重继承
int main() {
Derived d;
Base1* b1 = &d;
// 将指针从继承层级的一个分支 (Base1) 转换到另一个分支 (Base2)
Base2* b2 = dynamic_cast<Base2*>(b1);
if (b2) {
std::cout << "Cross cast from Base1 to Base2 successful!\n";
}
return 0;
}
3. const_cast
- 常量转换(最危险)
const_cast 是 C++ 中专门用于从指针或引用上添加或移除 const
或 volatile
限定符的类型转换操作。它允许修改底层对象,前提是该对象最初并未声明为 const
或 volatile
。
const_cast
应该用于"恢复对象的原始常量性",而不是"强行改变对象的常量性"。
错误的使用:
const int x = 10; // 原本就是const
int* bad_ptr = const_cast<int*>(&x);
*bad_ptr = 20; // 未定义行为!
相对安全的使用:
int y = 10; // 原本不是const
const int* const_ptr = &y; // 添加了const限定
// 移除我们刚才添加的const限定是安全的
int* good_ptr = const_cast<int*>(const_ptr);
*good_ptr = 20; // 安全,因为y原本就可修改
调用旧接口:
// 合法用途:调用旧接口
void oldLibFunction(char* str); // 参数不是 const
const char* my_str = "hello";
oldLibFunction(const_cast<char*>(my_str)); // 但仍然要确保函数不会修改字符串
4. reinterpret_cast
- 重解释转换(最底层)
将指针转换为整数,或将整数转换为指针
在不同类型指针之间进行转换(如
Foo*
->Bar*
)用于函数指针类型的转换
特点:极度危险,几乎不进行任何检查。可移植性差。
int i =20;
// 将整数的地址转换回整数本身
uintptr_t addr = reinterpret_cast<uintptr_t>(&i);
// 完全不同类型指针间的转换(通常非法,但有特定场景如内存分配器)
Foo* foo = new Foo;
Bar* bar = reinterpret_cast<Bar*>(foo); // 极度危险