C++ 强制类型转换
C++ 四大强制类型转换
转换类型 | 使用范围 | 访问权限 | 继承关系要求 | 多态支持 | 额外说明 |
---|---|---|---|---|---|
static_cast | 适用于相关类型之间的转换,如基类<->派生类指针转换、数值类型转换等 | 需满足访问权限(如基类继承方式) | 必须有继承关系,且访问权限允许 | 可用于多态类型,但不做运行时检查 | 编译时转换,效率高,但对非法转换不安全,向下转型可能导致未定义行为 |
dynamic_cast | 用于多态类型的安全向下转型或跨类层次指针转换 | 需满足访问权限 | 必须是多态类(含虚函数),有继承关系 | 支持,执行运行时类型检查 | 转换失败时,指针返回 nullptr ,引用抛异常,性能开销较大 |
const_cast | 用于去除或添加 const / volatile 修饰 | 无访问权限限制 | 不依赖继承关系 | 不涉及多态 | 只能修改类型修饰符,不能改变类型本质,去除 const 后修改对象导致未定义行为 |
reinterpret_cast | 任意类型指针或整数间的转换,极其底层和不安全 | 无访问权限限制 | 不要求继承关系 | 不支持 | 不安全,直接按二进制重新解释,通常用于系统底层、硬件交互等特殊场景,慎用 |
static_cast
编译期类型检查,适合“已知关系”的转换
error C2440: “static_cast”: 无法从“B *”转换为“A *”, 需要存在寄存关系
class A {};
class B {};
int main()
{B b;A* a = static_cast<A*>(&b);
}
reinterpret_cast
完全底层强转,仅改变解释方式,不改地址内容
不能用于普通类型转换,例如:int → float (float、int底层内存解析不一样)
const_cast
去除 const,解除只读属性
dynamic_cast
支持多态转换,需有虚函数,需要RTTI(运行阶段类型识别)
dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该运算符返回0(空指针);
典型实现步骤
当执行 dynamic_cast<DestType*>(srcPtr) 时,编译器生成的底层代码大致会做以下操作:
- (1) 检查空指针
如果 srcPtr 是 nullptr,直接返回 nullptr。 - (2) 检查静态类型兼容性
如果 DestType 和 srcPtr 的静态类型(编译时类型)无关(非继承关系),直接返回 nullptr。 - (3) 查询 RTTI
通过 srcPtr 对象的虚函数表(vtable)找到其 RTTI 信息(通常是 type_info 结构体),获取实际对象的动态类型。 - (4) 遍历继承树
向下转型(Downcast):检查 DestType 是否是实际对象的基类或派生类。如果是派生类,计算指针偏移量并调整。
侧向转型(Crosscast):在多重继承中,可能需要调整指针指向目标类的子对象。 - (5) 返回结果
如果类型兼容,返回调整后的指针。否则,返回 nullptr。
class B1 {
public:virtual void f1() {}int b1_data;
};class B2 {
public:virtual void f2() {}int b2_data;
};class D : public B1, public B2 {
public:virtual void f1() override {} // 覆盖 B1::f1virtual void fd() {} // D 的新虚函数int d_data;
};
int main()
{B2* d = new B2();B1* pb2 = dynamic_cast<B1*>(d);D* pb2s = static_cast<D*>(d);
}
pb2 不能转换成功,创建的B2类型不能转成B1类型
pb2s 能转换成功,但是运行的时候会产线未定义行为,因为分内存的时候只是分配了B2的内存大小,但是强制转成D,变成使用了D的内存大小,可能会访问非法内存
下图:
pb2s直接按编译向前减了8个字节(static_cast编译器的时候就进行转换了)
问题
为什么 static_cast 能正确处理多继承向上转型?
(1) 编译器已知继承关系
在编译时,编译器 完全知道 Derived 的继承结构(包括基类的顺序和偏移量)。
static_cast 直接根据继承关系计算指针调整,无需运行时信息。
(2) 指针调整是确定的
在多继承中,基类子对象的偏移量是编译期常量。例如:
Base1 的偏移量 = 0(首个子对象)。
Base2 的偏移量 = sizeof(Base1)。
static_cast 会根据目标基类类型,自动计算正确的偏移量。
(3) 不涉及运行时类型检查
static_cast 不依赖 RTTI(Run-Time Type Information),完全在编译期完成转换。
因此,即使没有虚函数(无虚表),static_cast 也能正确工作。
对比 dynamic_cast
dynamic_cast 也能向上转型,但它是为 向下转型(Downcasting) 设计的,向上转型时会退化为 static_cast:
dynamic_cast 的向上转型没有额外作用,反而可能误导代码读者(让人误以为需要运行时检查)。
static_cast 更清晰、更高效。
Base1* pb1 = dynamic_cast<Base1*>(&d); // 等价于 static_cast
为什么 static_cast 不能安全用于向下转型?
虽然 static_cast 能正确处理向上转型,但 向下转型(Downcasting) 时存在风险:
static_cast 不会检查 pb2 是否真的指向 Derived 对象,直接按继承关系调整指针,可能导致非法内存访问。此时必须用 dynamic_cast:
Base2* pb2 = new Base2; // 纯 Base2 对象
Derived* pd = static_cast<Derived*>(pb2); // 危险!