dynamic_cast是将一个基类对象指针(或引用)转换到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。

介绍

编辑

dynamic_cast运算符它涉及到编译器的属性设置,而且牵扯到的面向对象的多态性跟程序运行时的状态也有关系,所以不能完全的使用传统的转换方式来替代。但是也因此它是最常用,最不可缺少的一个运算符 [1]  。

与static_cast一样,dynamic_cast的转换也需要目标类型和源对象有一定的关系:继承关系。 更准确的说,dynamic_cast是用来检查两者是否有继承关系。因此该运算符实际上只接受基于类对象的指针和引用的类转换。从这个方面来看,似乎dynamic_cast又和reinterpret_cast是一致的,但实际上,它们还是存在着很大的差别。

语法

dynamic_cast <新类型> (表达式)。

用法

编辑

dynamic_cast <type-id> (expression)

该运算符把expression转换成type-id类型的对象。Type-id 必须是类的指针、类的引用或者void*;

如果 type-id 是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么 expression 也必须是一个引用。

dynamic_cast运算符可以在执行期决定真正的类型。如果 downcast 是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果 downcast 不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;

在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

classB

{

public:

int m_iNum;

virtual void foo();

};

 

classD:publicB

{

public:

char* m_szName[100];

};

 

void func(B* pb)

{

D* pd1=static_cast<D*>(pb);

D* pd2=dynamic_cast<D*>(pb);

}

在上面的代码段中,如果 pb 指向一个 D 类型的对象,pd1 和 pd2 是一样的,并且对这两个指针执行 D 类型的任何操作都是安全的;但是,如果 pb 指向的是一个 B 类型的对象,那么 pd1 将是一个指向该对象的指针,对它进行 D 类型的操作将是不安全的(如访问 m_szName),而 pd2 将是一个空指针。

另外要注意:B 要有虚函数,否则会编译出错;static_cast则没有这个限制。

这是由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表,没有定义虚函数的类是没有虚函数表的。

虚函数对dynamic_cast的作用

编辑

在C++的面对对象思想中,虚函数起到了很关键的作用,当一个类中拥有至少一个虚函数,那么编译器就会构建出一个虚函数表(virtual method table)来指示这些函数的地址,假如继承该类的子类定义并实现了一个同名并具有同样函数签名(function siguature)的方法重写了基类中的方法,那么虚函数表会将该函数指向新的地址。此时多态性就体现出来了:当我们将基类的指针或引用指向子类的对象的时候,调用方法时,就会顺着虚函数表找到对应子类的方法而非基类的方法。

当然虚函数表的存在对于效率上会有一定的影响,首先构建虚函数表需要时间,根据虚函数表寻到到函数也需要时间。

因为这个原因如果没有继承的需要,一般不必在类中定义虚函数。但是对于继承来说,虚函数就变得很重要了,这不仅仅是实现多态性的一个重要标志,同时也是dynamic_cast转换能够进行的前提条件。

假如去掉上个例子中Stranger类析构函数前的virtual,那么语句

Children* child_r =dynamic_cast<Children*> (stranger_r);

在编译期就会直接报出错误,具体原因不是很清楚,我猜测可能是因为当类没有虚函数表的时候,dynamic_cast就不能用RTTI来确定类的具体类型,于是就直接不通过编译。

这不仅仅是没有继承关系的类之间的情况,如果基类或者子类没有任何虚函数(如果基类有虚函数表,子类当然是自动继承了该表),当他们作为dynamic_cast的源类型进行转换时,编译也会失败。

这种情况是有可能存在的,因为在设计的时候,我们可能不需要让子类重写任何基类的方法。但实际上,这是不合理的。导师在讲解多态性的时候,时刻强调了一点:如果要用继承,那么一定要让析构函数是虚函数;如果一个函数是虚函数,那么在子类中也要是虚函数。

交叉转换

编辑

另外,dynamic_cast还支持交叉转换(cross cast)。如下代码所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

classA

{

public:

intm_iNum;

virtual void f(){}

};

class B:public A

{

};

class D:public A

{

};

void foo()

{

B*pb=new B;

pb->m_iNum=100;

//D*pd1=static_cast<D*>(pb);//compile error

D*pd2=dynamic_cast<D*>(pb);//pd2isNULL

delete pb;

}

在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错,而使用 dynamic_cast的转换则是允许的,结果是空指针。

运用实例

编辑

问题

1)什么时候应必须使用dynamic_cast

2)什么时候dynamic_cast可以使用static_cast代替

实例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

//TestCast.cpp:Definestheentrypointfortheconsoleapplication.

//#include"stdafx.h"

#include"stdlib.h" 

#include<tchar.h> //工程文件中可直接使用,无需定义

#include<iostream>

using namespace std;

 

class Base

{

public:

    virtual void f() {cout << "Base::f" << endl;}

    void f1() {cout << "Base::f1" << endl;}

private:

    double x;

    double y;

};

 

class Derived : public Base

{

public:

    virtual void f() {cout << "Derived::f" << endl;}

    virtual void k() {cout << "Derived::k" << endl;}

private:

    double z;

};

 

class Base1

{

public:

    virtual void g() {cout << "Base1::g" << endl;}

    void g1() {cout << "Base1::g1" << endl;}

};

 

class Derived1 : public Base, public Base1

{

public:

    virtual void f() {cout << "Derived1::f" << endl;}

    virtual void h() {cout << "Derived1::h" << endl;}

};

 

void Test1()

{

    //对于单继承,

    //如果pD真的指向Derived,用dynamic_cast和static_cast效果相同

    Base* pD = new Derived;

     

    Derived* pD1 = dynamic_cast<Derived*>(pD);

    pD1->f();

    pD1->k();

    pD1->f1();

     

    Derived* pD2 = static_cast<Derived*>(pD);

    pD2->f();

    pD2->k();

    pD2->f1();

     

    //但是如果pB不是真的指向Derived,则用dynamic_cast则返回NULL,能够更早的禁止error的发生,

    //如果用static_cast虽然返回的不为NULL,但是运行时可能抛出exception。

    /**/Errorcode

    //Base* pB = new Base();

    //Derived* pD3 = static_cast<Derived*>(pB);

    //pD3->f();

    //pD3->k();

    //pD3->f1();

    //Derived*pD4 = dynamic_cast<Derived*>(pB);

    //pD4->f();

    //pD4->k();

    //pD4->f1();

}

 

void Test2()

{

    //对于多重继承,

    //如果pD真的指向的是Derived1,使用dynamic_cast和static_cast都可以转化为Derived1,

    //但是如果要转化为Base的兄弟类Base1,必须使用dynamic_cast,使用static_cast不能编译。

    Base* pD = new Derived1;

    Derived1* pD1 = dynamic_cast<Derived1*>(pD);

    pD1->f();

    pD1->h();

    pD1->f1();

     

    Derived1* pD2 = static_cast<Derived1*>(pD);

    pD2->f();

    pD2->h();

    pD2->f1();

     

    Base1* pB1 = dynamic_cast<Base1*>(pD);

    pB1->g();

     

    /**/errorcannotcompiler

    //Base1* pB2 = static_cast<Base1*>(pD);

    //pB2->g();

    //当然对于pB不是真的指向Derived1,想要转化为Derived1或Base的兄弟类Base1,情况与Test1中的error情况相同。

}

 

int _tmain(int argc, _TCHAR*argv[])

{

    Test1();

    Test2();

    return 0 ;

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值