Boost C++ 库

...世界上最受尊敬和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

PrevUpHomeNext

多态转换

polymorphic_downcast
polymorphic_cast
polymorphic_pointer_cast

多态对象(定义了至少一个虚函数的类的对象)的指针有时会被向下转换或交叉转换。向下转换意味着从基类转换为派生类。交叉转换意味着跨越继承层次结构图进行转换,例如在 Y 形层次结构中从一个基类转换为另一个基类。

这种转换可以使用旧式转换来完成,但这种方法永远不值得推荐。旧式转换严重缺乏类型安全性,可读性差,并且难以使用搜索工具定位。

C++ 内置的 static_cast 可以用于高效地向下转换多态对象的指针,但是对于指针实际指向错误的派生类的情况,它不提供错误检测。polymorphic_downcast 模板在非调试编译中保留了 static_cast 的效率,但在调试编译中,通过 assert() 添加了安全性,以确保 dynamic_cast 成功。

polymorphic_downcast 应该用于您确信应该成功的向下转换。错误检查仅在未定义 NDEBUG 的翻译单元中执行,通过

assert( dynamic_cast<Derived>(x) == x )

其中 x 是源指针。这种方法确保不仅返回非零指针,而且在多重继承的情况下也是正确的。尝试使用 polymorphic_downcast 进行交叉转换将无法编译。

[Warning] 警告

由于 polymorphic_downcast 使用 assert(),如果 NDEBUG 在不同的翻译单元中定义不一致,它会违反单一定义规则 (ODR)。请参阅 ISO Std 3.2

示例:
#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) 在指针上使用,如果 p0,则会导致未定义的行为。polymorphic_cast 模板对指针执行 dynamic_cast,如果 dynamic_cast 返回 0,则抛出异常。

对于交叉转换,或者当转换的成功只能在运行时知道时,或者当效率不重要时,polymorphic_cast 是首选。

C++ 内置的 dynamic_cast 必须用于转换引用而不是指针。它也是唯一可以用于检查是否支持给定接口的转换;在这种情况下,返回 0 不是错误条件。

虽然 polymorphic_downcastpolymorphic_cast 仅适用于内置指针类型,但 polymorphic_pointer_downcastpolymorphic_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_ptrboost::shared_ptrboost::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);
  ...
}

PrevUpHomeNext