了解C++中的四种 Cast
static_cast
static_cast
是 C++ 中用于执行显式类型转换的操作符,它允许你在编译时进行类型转换,而不是在运行时。static_cast
通常用于执行安全的类型转换,比如基本数据类型的转换、类层次结构中的向上转换(将派生类指针或引用转换为基类指针或引用),以及向下转换(在你知道类型确实匹配的情况下,将基类指针或引用转换为派生类指针或引用)。
- 基本数据类型转换:
int a = 10;
char c = static_cast<char>(a); // int 到 char 的转换
- 类层次结构中的向上转换(安全):
class Base {};
class Derived : public Base {};
Derived* d = new Derived();
Base* b = static_cast<Base*>(d); // Derived* 到 Base* 的转换
- 类层次结构中的向下转换(不安全,除非你知道确实类型匹配):
Base* b = new Derived();
Derived* d = static_cast<Derived*>(b); // Base* 到 Derived* 的转换
// 只有当你确信 b 指向的是 Derived 对象时,这种向下转换才是安全的
//如果你需要确保安全性,应该使用 dynamic_cast,它会检查转换是否合法,并在不合法时返回 nullptr(对于指针)或抛出 std::bad_cast 异常(对于引用)。
- 将
void*
转换为具体类型:
void* ptr = ...;
int* intPtr = static_cast<int*>(ptr); // void* 到 int* 的转换
使用 static_cast
时需要注意以下几点:
static_cast
不会进行运行时类型检查,因此如果转换不正确,可能会导致未定义行为。static_cast
不能用于删除const
、volatile
或__unaligned
修饰符,如果要去除这些修饰符,应该使用const_cast
。static_cast
不能用于将非类类型转换为类类型,这种转换通常是不安全的。
dynamic_cast
dynamic_cast
是 C++ 中用于在类层次结构中进行安全向下转换(downcasting)的操作符。它允许你将基类指针或引用转换为派生类指针或引用,并且在转换过程中进行运行时类型检查(RTTI)。如果转换是不合法的,即基类指针实际上并不指向派生类对象,那么 dynamic_cast
会返回 nullptr
(对于指针)或抛出 std::bad_cast
异常(对于引用)。
RTTI
RTTI(Run-Time Type Information,运行时类型信息)是 C++ 中的一种机制,它允许程序在运行时获取对象的类型信息。RTTI 主要用于两个方面:类型识别和类型转换。
- 类型识别:RTTI 允许程序检查一个对象的具体类型,这是通过
typeid
操作符实现的。typeid
返回一个type_info
对象,该对象包含有关类型的详细信息,如类型名称。 - 类型转换:RTTI 允许程序在运行时安全地转换对象类型,这是通过
dynamic_cast
操作符实现的。dynamic_cast
可以将对象指针或引用安全地转换为目标类型的指针或引用,如果在转换过程中类型不匹配,对于指针返回nullptr
,对于引用抛出std::bad_cast
异常。
在RTTI的实现中,RTTICompleteObjectLocator
是一个重要的结构体,它包含了几个关键成员:
typeinfo
:这是一个包含类型名和父类类型名的结构体,通过typeid
函数可以获取对象的typeinfo
,从而得到对象的类型信息。class descriptor
:这是一个描述继承关系和信息的结构体。它记录了基类的个数、基类的数组、以及该类是否使用了多继承或虚继承。offset
:这是虚函数指针相对于类头部的偏移量。它可以用来优化动态类型转换(如dynamic_cast
)的速度,因为当将基类的指针转换为派生类的指针时,可以直接通过this
指针加上这个偏移量来找到正确的位置。
RTTI的使用场景是,在代码运行时并不知道一个指针指向的是子类还是基类对象,就需要通过RTTI得到其信息,比如typeid和dynamic_cast。RTTI存在于每个虚函数表首地址往上的四个字节,所以父类至少要有一个虚函数,否则无法识别正确类型。
以下是一些需要注意的点:
dynamic_cast
仅适用于包含至少一个虚函数的类层次结构,因为它是通过虚函数表来实现运行时类型检查的。dynamic_cast
用于向下转换时比static_cast
安全,因为它会检查转换的合法性。dynamic_cast
的性能成本比static_cast
高,因为它需要在运行时进行类型检查。dynamic_cast
可以用于交叉转换,即在不同继承层次的派生类之间进行转换。- 有个值得注意的点,菱形继承和侧向转换时,
dynamic_cast
可能会因二义性造成转换失败。
class Base {
public:
virtual void f() {}
};
class Derived : public Base {
public:
void f() override {}
};
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 安全的向下转换
if (d) {
// 转换成功,d 指向 Derived 对象
} else {
// 转换失败,b 不指向 Derived 对象
}
const_cast
const_cast
是 C++ 中用于去除 const 特性的类型转换操作符。它允许你将 const 对象的指针或引用转换为非 const 对象的指针或引用,从而使得原本不可修改的对象变得可修改。这种转换是在编译时进行的,但它不会改变对象本身的 const 性质,只是让编译器允许你通过指针或引用修改对象。
只能修改常量指针,不能修改指针常量。
const_cast
仅用于去除 const 或 volatile 特性,不能用于其他类型的转换。const_cast
只适用于指针或引用,不能直接应用于对象。- 使用
const_cast
去除 const 特性是危险的,因为它可能会导致对 const 对象的意外修改,从而破坏程序的不变性和安全性。
class MyClass {
public:
void modify() { /* ... */ }
};
int main()
{
const MyClass obj;
MyClass* nonConstPtr = const_cast<MyClass*>(&obj);
nonConstPtr->modify(); // 现在 可以调用非 const 成员函数了
// 使用 const_cast 去除 volatile 特性
volatile int vi = 1;
int* nonVolatilePtr = const_cast<int*>(&vi);
*nonVolatilePtr = 2; // 现在 可以修改 volatile 变量了
}
reinterpret_cast
reinterpret_cast
是 C++ 中用于执行非类型安全的低级类型转换的操作符。它允许你将一个指针、引用或数据类型的值转换为另一个指针、引用或数据类型的值,除了无法去掉const属性,可以随便转换,但不会进行任何类型检查或运行时类型信息(RTTI)检查。
int* ptr = new int(10);
// 将 int 指针转换为 int 类型的整数
int intValue = reinterpret_cast<int>(ptr);
// 将 int 类型的整数转换回 int 指针
int* newPtr = reinterpret_cast<int*>(intValue);
// 将 int 指针转换为 void 指针
void* voidPtr = reinterpret_cast<void*>(ptr);
// 将 void 指针转换回 int 指针
int* anotherPtr = reinterpret_cast<int*>(voidPtr);