理解C++运行时类型转换符:dynamic_cast

目录

1.  dynamic_cast 语法格式

2.  dynamic_cast 用法示例

2.1  向上转换

2.2  void* 转换

2.3  向下转换

2.4  多继承下的转换


1.  dynamic_cast 语法格式

dynamic_cast < type-id > ( expression )

dynamic_cast 运算符接受两个操作数:一个用 <> 括起来的类型 type-id (类指针、引用、或void*),以及一个用 () 括起来的指针或引用dynamic_cast 不允许意外违反私有和受保护基类的保护(译注:这是与C语言风格的强制转换的主要区别,因此涉及强制类对象指针转换时推荐这种方法)由于用作向上转型(即子类向基类的转换)的 dynamic_cast 与简单赋值完全相同,因此它不涉及任何开销,并且对其词法上下文很敏感。

    dynamic_cast 的目的是处理编译器无法确定转换正确性的情况dynamic_cast 需要一个指向多态类型的指针或引用(即当向下转换时(基类向派生类转换), expression 必须是多态的引用或指针)以便进行向下转换或交叉转换。要求指针的类型具有多态性简化了 dynamic_cast 的实现,因为它可以轻松找到保存有关对象类型的必要信息的位置。因此,当向下转换时 ,dynamic_cast 要求的指针或者引用必须具有 virtual 基函数

    即,当向上转换时,等同于赋值;当向下转换时,派生类必须是多态的,即基类必须有virtual函数

2.  dynamic_cast 用法示例

2.1  向上转换

如果 type-id expression(表达式)一个直接或间接基类的无歧义指针则指向类型  type-id 的唯一子对象(即基类对象)的子针就是操作的结果。例如:

C++

// dynamic_cast_1.cpp

// compile with: /c

class B { };

class C : public B { };

class D : public C { };

void f(D* pd) {

   C* pc = dynamic_cast<C*>(pd);   // ok: C 是直接基类

                                   // pc 指向pd C 子对象

   B* pb = dynamic_cast<B*>(pd);   // ok: B D的间接基类

                                   // pb 指向pd B 指对象

}

这种类型称为“向上(基类)转换”,因为它在类层级结构中将一个指针从派生类向基类移动(而类层级结构基类在上),向上转换是隐式转换。

2.2  void* 转换

         如果 type-id void*,则系统通过运行时检查去确定 expression(表达式)的实际类型。其结果是指向expression所指向的完整对象的指针(而不是子对象)。

C++

// dynamic_cast_2.cpp

// compile with: /c /GR

class A { virtual void f(){};};

class B { virtual void f(){};};

void f() {

   A* pa = new A;

   B* pb = new B;

   void* pv = dynamic_cast<void*>(pa);

   // pv 此时指向类型A的一个对象

   pv = dynamic_cast<void*>(pb);

   // pv 此时指向类型B的一个对象

}

如果 type-id 不是 void*,则会进行运行时检查,查看expression指向的对象是否可以转换为 type-id 指向的类型。

2.3  向下转换

         如果 expression 的类型是type-id类型的基类则会进行运行时检查,查看是否expression实际上指向type-id类型的完整对象。若成立,结果是指向type-id类型的完整对象的指针。例如:

C++

// dynamic_cast_3.cpp

// compile with: /c /GR

class B { virtual void f(){};};

class D : public B { virtual void f(){};};

void f() {

   B* pb = new D;   // 不明显但尚可

   B* pb2 = new B;

   D* pd = dynamic_cast<D*>(pb);   // ok: pb 实际上指向 D

   D* pd2 = dynamic_cast<D*>(pb2);   // pb2 指向 B 而非 D,因此结果为 null

}

这种类型的转换称为“向下转换”,因为它在类层级结构上将指针从基类向派生类移动。

2.4  多继承下的转换

         在多继续的情况下,有可能会引入歧义。考虑如上图中的类层级结构。

指向类型D 的对象的指针,可以安全地转化为B或C。然而,若 D 转化为指向A的对象,则结果是A的哪个对象(即走哪条路径转换呢)?这会导致抛出一个表示歧义错误。为了避开这个错误,可以执行两个无歧义转换。例如:

C++

// dynamic_cast_4.cpp

// compile with: /c /GR

class A { virtual void f(){};};

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

class C : public A { virtual void f(){};};

class D : public B, public C { virtual void f(){};};

void f() {

   D* pd = new D;

   A* pa = dynamic_cast<A*>(pd);   // C4540, 运行时歧义转换失败

                                  // 返回 nullptr

   B* pb = dynamic_cast<B*>(pd);   // 先转换为 B

   A* pa2 = dynamic_cast<A*>(pb);   // ok: 无歧义转换

}

当使用virtual 基类的时候,会引入更多的歧义。考虑下面图表给出的类层级结构。 

------------------表示virtual基类的类层级结构------------------------

         在这个图表示的类层级结构中,A 是一个 virtual基类。已知类 E的一个实例以及一个指向A子对象的指针,一个向B 的指针dynamic_cast转换会因为歧义而失败。必须先转换回完整的 E 对象,然后按照非歧义的转换方法按类层级结构向上转换,直到转换为正确的B对象。

         考虑如下所示的类层级结构图。

------------------表示重复基类的类层级结构(E是多重继续的基类)-----------------

         已知一个类型E的对象,以及一个指向 D子对象的一个指针,要从 D 子对象导航到最左边的 A 子对象,可以进行三次转换。您可以执行从 D 指针到 E 指针的 dynamic_cast 转换,然后执行从 E 到 B 的转换(dynamic_cast 或隐式转换),最后执行从 B 到 A 的隐式转换。例如:

C++:

// dynamic_cast_5.cpp

// compile with: /c /GR

class A { virtual void f(){};};

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

class C : public A { };

class D { virtual void f(){};};

class E : public B, public C, public D { virtual void f(){};};

void f(D* pd) {

   E* pe = dynamic_cast<E*>(pd);

   B* pb = pe;   // 向上转换, 隐式转换

   A* pa = pb;   // 向上转换,隐式转换

}

dynamic_cast运算符也可用于执行“交叉转换”。使用同样的类层级结构,可以将指针从 B 子对象转换为 D 子对象,只要完整对象是 E 类型即可。

考虑到交叉转换,只需两步就可以将指向 D 的指针转换为指向最左侧子对象 A 的指针。您可以执行从 D 到 B 的交叉转换,然后执行从 B 到 A 的隐式转换。例如:

C++

// dynamic_cast_6.cpp

// compile with: /c /GR

class A { virtual void f(){};};

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

class C : public A { };

class D { virtual void f(){};};

class E : public B, public C, public D { virtual void f(){};};

void f(D* pd) {

   B* pb = dynamic_cast<B*>(pd);   // 交叉转换

   A* pa = pb;   // 向上转换,隐式转换

}

注: pb的值为null 。

空指针值通过 dynamic_cast 转换为目标类型的空指针值。

         使用 dynamic_cast < type-id > ( expression ) 时,如果无法将 expression 安全地转换为类型 type-id,则运行时检查会导致转换失败。例如:

C++

// dynamic_cast_7.cpp

// compile with: /c /GR

class A { virtual void f();};

class B { virtual void f();};

void f() {

   A* pa = new A;

   B* pb = dynamic_cast<B*>(pa);   // 运行时转换失败, 不安全;

   // B 不从A派生

}

转换为指针类型失败时,其值为空指针。转换为引用类型失败时,将引发 bad_cast 异常如果 expression 未指向或引用有效对象则会引发 __non_rtti_object 异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值