dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用。
dynamic_cast用于类继承层次间的指针或引用转换。主要还是用于执行“安全的向下转型(safe downcasting)”,也即是基类对象的指针或引用转换为同一继承层次的其他指针或引用。
至于“向上转型”(即派生类指针或引用类型转换为其基类类型),本身就是安全的,尽管可以使用dynamic_cast进行转换,但这是没必要的, 普通的转换已经可以达到目的,毕竟使用dynamic_cast是需要开销的。
特性
使用dynamic_cast转换的Base类至少带有一个虚函数。
dynamic_cast依赖于RTTI信息,当一个类中拥有至少一个虚函数的时候,编译器会为该类构建出一个虚函数表(virtual method table),虚函数表记录了虚函数的地址。如果该类派生了其他子类,且子类定义并实现了基类的虚函数,那么虚函数表会将该函数指向新的地址。虚表是C++多态实现的一个重要手段,也是dynamic_cast操作符转换能够进行的前提条件。当类没有虚函数表的时候(也即一个虚函数都没有定义),dynamic_cast无法使用RTTI,不能通过编译。
在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型,这种检查不是语法上的,而是真实情况的检查,在运行时进行,会有一定的性能损耗。
当然,虚函数表的建立对效率是有一定影响的,构建虚函数表、由表查询函数 都需要时间和空间上的消耗。所以,除了必须声明virtual(对于一个多态基类而言),不要轻易使用virtual函数。
dynamic_cast作用于指针、引用的转换
指针:dynamic_cast在运行时做检查,转换失败时,返回结果为nullptr;
引用:指针一样,引用的向下转型也可以分为两种情况,与指针不同的是,并不存在空引用,所以引用的dynamic_cast检测失败时会抛出一个bad_cast异常。
class Entity
{
public:
virtual void PrintName() {}
};
class Player : public Entity
{
};
class Enemy : public Entity
{
};
int main()
{
{
Player* player = new Player();
Entity* actuallyPlayer = player;//向上转换,安全的,不需要dynamic_cast
Entity* actuallyEnemy = new Enemy();
Player* p0 = dynamic_cast<Player*>(actuallyPlayer); // 成功
Player* p1 = dynamic_cast<Player*>(actuallyEnemy); // 失败,结果p1会是nullptr
}
{
Player p2;
Entity &entity = p2;
Player& p3 = dynamic_cast<Player&>(entity);// 成功
}
{
Entity e0;
Entity& e1 = e0;
try {
Player& p = dynamic_cast<Player&>(e1); // 失败,抛出异常bad_cast
}
catch (bad_cast)
{
std::cout << "转化失败,抛出bad_cast异常" << endl;
}
}
std::cin.get();
}