多态对象(定义了至少一个虚函数的类的对象)的指针有时会被向下转换或交叉转换。向下转换意味着从基类转换为派生类。交叉转换意味着跨越继承层次结构图进行转换,例如在 Y
形层次结构中从一个基类转换为另一个基类。
这种转换可以使用旧式转换来完成,但这种方法永远不值得推荐。旧式转换严重缺乏类型安全性,可读性差,并且难以使用搜索工具定位。
C++ 内置的 static_cast
可以用于高效地向下转换多态对象的指针,但是对于指针实际指向错误的派生类的情况,它不提供错误检测。polymorphic_downcast
模板在非调试编译中保留了 static_cast
的效率,但在调试编译中,通过 assert()
添加了安全性,以确保 dynamic_cast
成功。
polymorphic_downcast
应该用于您确信应该成功的向下转换。错误检查仅在未定义 NDEBUG
的翻译单元中执行,通过
assert( dynamic_cast<Derived>(x) == x )
其中 x
是源指针。这种方法确保不仅返回非零指针,而且在多重继承的情况下也是正确的。尝试使用 polymorphic_downcast
进行交叉转换将无法编译。
![]() |
警告 |
---|---|
由于 |
#include <boost/polymorphic_cast.hpp> ... class Fruit { public: virtual ~Fruit(){}; ... }; class Banana : public Fruit { ... }; ... void f( Fruit * fruit ) { // ... logic which leads us to believe it is a Banana Banana * banana = boost::polymorphic_downcast<Banana*>(fruit); ... }
C++ 内置的 dynamic_cast
可以用于多态对象指针的向下转换和交叉转换,但是以返回值 0 的形式进行错误通知不太方便测试,或者更糟糕的是,容易忘记测试。在引用上工作的 dynamic_cast
的抛出形式可以通过丑陋的表达式 &dynamic_cast<T&>(*p)
在指针上使用,如果 p
是 0
,则会导致未定义的行为。polymorphic_cast
模板对指针执行 dynamic_cast
,如果 dynamic_cast
返回 0,则抛出异常。
对于交叉转换,或者当转换的成功只能在运行时知道时,或者当效率不重要时,polymorphic_cast
是首选。
C++ 内置的 dynamic_cast
必须用于转换引用而不是指针。它也是唯一可以用于检查是否支持给定接口的转换;在这种情况下,返回 0 不是错误条件。
虽然 polymorphic_downcast
和 polymorphic_cast
仅适用于内置指针类型,但 polymorphic_pointer_downcast
和 polymorphic_pointer_cast
是更通用的版本,支持任何指针类型,只要以下表达式有效
对于 polymorphic_pointer_downcast
static_pointer_cast<Derived>(p); dynamic_pointer_cast<Derived>(p);
对于 polymorphic_pointer_cast
dynamic_pointer_cast<Derived>(p); !p; // conversion to bool with negation
这包括 C++ 内置指针、std::shared_ptr
、boost::shared_ptr
、boost::intrusive_ptr
等。
#include <boost/polymorphic_pointer_cast.hpp> class Fruit { public: virtual ~Fruit(){} }; class Banana : public Fruit {}; // Use one of these: using FruitPtr = Fruit*; using FruitPtr = std::shared_ptr<Fruit>; using FruitPtr = boost::shared_ptr<Fruit>; using FruitPtr = boost::intrusive_ptr<Fruit>; void f(FruitPtr fruit) { // ... logic which leads us to believe it is a banana auto banana = boost::polymorphic_pointer_downcast<Banana>(fruit); ... }