Sun Studio 12:C++ 用户指南

9.4 动态强制类型转换

指向类的指针(或引用)可以实际指向(引用)从该类派生的任何类。有时希望指向完全派生类的指针,或指向完整对象的某些其他子对象。动态强制类型转换可以实现这些功能。


注 –

在兼容模式 (-compat[=4]) 下编译时,如果程序使用动态强制类型转换,则必须使用 -f eatures=rtti 进行编译。


动态强制类型转换可将指向一个类 T1 的指针(或引用)转换到指向另一个类 T2 的指针(或引用)。T1T2 必须属于同一分层结构,类必须是可访问的(通过公共派生),并且转换必须明确。此外,除非转换是从派生类到其一个基类,否则,包括 T1T2 的分层结构的最小部分必须是多态的(至少有一个虚函数)。

在表达式 dynamic_cast<T>(v) 中,v 是要进行强制类型转换的表达式,T 是要转换到的类型。T 必须是指向完整类的类型(其定义可见)的指针或引用,或指向 cv void (其中 cv 是空字符串)、constvolatileconst volatile 的指针。

9.4.1 将分层结构向上强制类型转换

对分层结构进行向上强制类型转换时,如果 T 指向(或引用) v 所指向(引用)类型的基类,则该转换等效于 static_cast<T>(v)

9.4.2 强制类型转换到 void*

如果 Tvoid*,则结果是指向完整对象的指针。也就是说,v 可能指向某完整对象的其中一个基类。在这种情况下,dynamic_cast<void*>(v) 的结果如同将 v 沿分层结构向下转换到完整对象(无论什么对象)的类型,然后转换到 void*

强制类型转换到 void* 时,分层结构必须是多态的(有虚函数)。

9.4.3 将分层结构向下或交叉强制类型转换

向下或交叉强制类型转换分层结构时,分层结构必须是多态的(具有虚函数)。结果在运行时检查。

对分层结构向下或交叉强制类型转换时,有时不能从 v 转换到 T。例如,尝试的转换可能不明确,T 可能无法访问,或 v 可能未指向(或引用)必要类型的对象。如果运行时检查失败且 T 是指针类型,则强制类型转换表达式的值是 T 类型的空指针。如果 T 是引用类型,不会返回任何值(没有 C++ 中的空引用),并且会抛出标准异常 std::bad_cast

例如,此公共派生的示例成功:


#include <assert.h>
#include <stddef.h> // for NULL

class A {public: virtual void f();};
class B {public: virtual void g();};
class AB: public virtual A, public B {};

void simple_dynamic_casts()
{
  AB ab;
  B* bp = &ab;        // no casts needed
  A* ap = &ab;
  AB& abr = dynamic_cast<AB&>(*bp);  // succeeds
  ap = dynamic_cast<A*>(bp);         assert(ap!= NULL);
  bp = dynamic_cast<B*>(ap);         assert(bp!= NULL);
  ap = dynamic_cast<A*>(&abr);       assert(ap!= NULL);
  bp = dynamic_cast<B*>(&abr);       assert(bp!= NULL);
}

但此示例失败,因为基类 B 不可访问。


#include <assert.h>
#include <stddef.h> // for NULL
#include <typeinfo>

class A {public: virtual void f() {}};
class B {public: virtual void g() {}};
class AB: public virtual A, private B {};

void attempted_casts()
{
  AB ab;
  B* bp = (B*)&ab; // C-style cast needed to break protection
  A* ap = dynamic_cast<A*>(bp); // fails, B is inaccessible
  assert(ap == NULL);
  try {
    AB& abr = dynamic_cast<AB&>(*bp); // fails, B is inaccessible
  }
  catch(const std::bad_cast&) {
    return; // failed reference cast caught here
  }
  assert(0); // should not get here
}

如果在一个单独的基类中存在虚拟继承和多重继承,那么实际动态强制类型转换必须能够识别出唯一的匹配。如果匹配不唯一,则强制类型转换失败。例如,假定有如下附加类定义:


class AB_B:     public AB,        public B {};
class AB_B__AB: public AB_B,      public AB {};

示例:


void complex_dynamic_casts()
{
  AB_B__AB ab_b__ab;
  A*ap = &ab_b__ab;
                    // okay: finds unique A statically
  AB*abp = dynamic_cast<AB*>(ap);
                    // fails: ambiguous
  assert(abp == NULL);
                    // STATIC ERROR: AB_B* ab_bp = (AB_B*)ap;
                    // not a dynamic cast
  AB_B*ab_bp = dynamic_cast<AB_B*>(ap);
                    // dynamic one is okay
  assert(ab_bp!= NULL);
}

dynamic_cast 返回的空指针错误可用作两个代码体之间的条件,一个用于类型确定正确时处理强制类型转换,另一个用于类型确定错误时处理强制类型转换。


void using_dynamic_cast(A* ap)
{
  if (AB *abp = dynamic_cast<AB*>(ap))
    {            // abp is non-null,
                 // so ap was a pointer to an AB object
                 // go ahead and use abp
      process_AB(abp);}
  else
    {          // abp is null,
               // so ap was NOT a pointer to an AB object
               // do not use abp
      process_not_AB(ap);
    }
}

在兼容模式 (-compat[=4]) 下,如果尚未使用 -features=rtti 编译器选项启用运行时类型信息,则编译器将 dynamic_cast 转换到 static_cast 并发出警告。

如果禁用了异常,编译器会将 dynamic_cast<T&> 转换到 static_cast<T&> 并发出警告。(对于引用类型,dynamic_cast 要求运行时发现转换无效的情况下抛出异常。)有关异常的信息,请参见7.5.3 诊断有问题的搜索

动态强制类型转换需要比对应的设计模式慢,例如虚函数的转换。请参见由 Erich Gamma 编著的《Design Patterns: Elements of Reusable Object-Oriented Software》(Addison-Wesley 出版,1994)。