C++学习之显示类型转换与运行时类型识别RTTI

static_cast

const_cast

reinterpret_cast

运行时类型识别(RTTI)

dynamic_cast

哪种情况下dynamic_cast和static_cast使用的情况一样?

什么情况下使用dynamic_cast代替虚函数?

typeid

命名的强制类型转换形式如下:

cast_name<type>(expression);

其中:cast_name指static_cast、dynamic_cast、const_cast、reinterpret_cast中的一种;

type指要转换的目标类型;

expression指要转换的值或表达式。

static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。同时,对于编译器无法自动执行的类型转换也非常有用。static_cast不能转换掉expression的const、volitale、或者__unaligned属性。没有运行时类型检查来保证转换的安全性。

它主要有如下几种用法:

①用于类层次结构中基类和子类之间指针或引用的转换。

进行上行转换(把子类的指针或引用转换成基类表示)是安全的;

进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。

②用于基本数据类型之间的转换。如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。

③把空指针转换成目标类型的空指针。

④把任何类型的表达式转换成void类型。

如:

【代码1】


1

2

3

double *d;

void* p = &d; //正确,任何"非常量对象"的地址都能存入void*

double* dp = static_cast<double*>(p);//正确,将void*转换为初始指针类型

const_cast:只改变运算对象的底层const性质,不改变表达式(运算对象)的类型。可用于增加/去除运算对象的const属性。同时,只能使用const_cast来进行更改const属性,其他任何形式的命名强制类型转换都会引起编译器错误。如:

【代码2】


1

2

3

4

5

const char *pc;

char *p = const_cast<char*>(pc);//正确:但通过p写值是未定义的行为

char *q = static_cast<char*>(pc);//错误:static_cast不能转换const性质

static_cast<string>(pc);//正确:字符串字面值转换为string类型

const_cast<string>(pc); //错误:const_cast只用来改变常量属性

const_cast常常用于函数重载的上下文中。如:

【代码3】


1

2

3

4

5

6

7

8

9

10

11

//比较两个string对象的长度,返回较短的那个引用

const string &shorterString(const string &s1, const string &s2){

    return s1.size()<=s2.size() ? s1 : s2;

}

string & shorterString(string &s1, string &s2){

    //调用const版本,先将s1和s2转换为const版本,返回的引用 r 为const版本

    auto &r = shorterString(const_cast<const string&>(s1),

                            const_cast<const string&>(s2));

    return const_cast<string&>(r);  //再次使用const_cast消除掉r的const属性,得到一个普通的引用

}

reinterpret_cast:C++ Primer中解释:通常为运算对象的位模式提供较低层次上的重新解释。不懂~~有如下例子:

【代码4】


1

2

int *ip;

char *pc = reinterpret_cast<char*>(ip);

在代码4中,必须牢记pc实际上指向的是一个int而不是字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。如:


1

string str(pc); //错误!

谨记:使用reinterpret_cast是非常危险的!要想安全的使用,必须对涉及的类型和编译器实现转换的过程都非常了解。所以,还是尽量不要使用的好!

同时,所有的强制类型转换,能不使用的情况尽量不要使用,因为其干扰了正常的类型检查。

运行时类型识别(RTTI):(由两个运算符实现)

typeid运算符,用来返回表达式的类型;

dynamic_cast运算符,用于将基类的指针或引用安全的转换成派生类的指针或引用。

这两个运算符特别适用于以下情况:使用‘基类对象’的‘指针或引用’执行某个‘派生类操作’并且’该操作‘不是‘虚函数’。

dynamic_cast运算符的使用形式:


1

2

3

4

//以下形式中:type必须为一个类类型,且通常该类型应该含有虚函数

dynamic_cast<type *>(e);  //e必须是一个有效的指针;转换失败时结果为0

dynamic_cast<type &>(e);  //e必须为左值;转换失败时抛出bad_cast异常

dynamic_cast<type &&>(e); //e不能为左值;转换失败时抛出bad_cast异常

其中,e的类型必须满足以下三个条件中的任一个:

  1. e的类型是目标type的公有派生类;
  2. e的类型是目标type的公有基类;
  3. e的类型是目标type的类型。

如果不满足上述三个中的任一个条件,则转换失败。同时,当使用dynamic_cast对一个空指针执行转换时,结果是所需类型的空指针。

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

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。如:

【代码5】


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

class A{

public:

    A();

    ~A();

    virtual void fun1();

    void fun2();

};

class B:public A{

    B();

    ~B();

    virtual void fun3();

    void fun4();

};

void f1(const A &a){ //引用类型的dynamic_cast

    try{

        const B &b = dynamic_cast<const B &>(a);

        //使用a引用的B对象

    }catch(std::bad_cast){

        //处理类型转换失败的情况

    }

}

  

void f2(){//指针类型的dynamic_cast

    A *ap = new A();//使用基类对象的【指针】

    if(B *bp = dynamic_cast<B *>(ap)){//动态转换ap为指向B类型的指针;成功则继续,不成功则为0

        bp->fun4();     //执行某个‘派生类操作’并且’该操作‘不是‘虚函数’

        //其他操作

    }else{

        //使用ap指向的A对象

    }

    delete ap;  ap=NULL;

}

如果对无继承关系或者没有虚函数的对象指针进行转换、基本类型指针转换以及基类指针转换为派生类指针,都不能通过编译。

哪种情况下dynamic_cast和static_cast使用的情况一样?

【代码6】


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

class C{

    //类内定义

};

class D:public A, public C{ //多重继承

    //类内定义

};

 

void f3(){ //单继承情况下

    //如果ap指向的即为B的对象,则使用dynamic_cast和static_cast效果一样

    A *ap = new B();

    B *bp = dynamic_cast<B *>(ap);  //A要有虚函数,否则使用dynamic_cast会产生编译错误,

    B *bp1 = static_cast<B *>(ap);  //static_cast则没有这个限制

    delete ap; delete bp; delete bp1;

    ap=bp=bp1=NULL;  //防止野指针

    //如果ap指向的不为B的对象,则用dynamic_cast返回NULL,能够更早的禁止error的发生;

    //如果用static_cast返回的不为NULL,但是运行时可能抛出异常

    A *ap = new A();

    B *bp = dynamic_cast<B *>(ap); //正确,但bp指向的为NULL

    B *bp1 = static_cast<B *>(ap); //错误,bp1在运行时可能会抛出异常

    delete ap; delete bp; delete bp1;

    ap=bp=bp1=NULL;  //防止野指针

}

void f4(){  //多继承情况下

    //如果ap指向的即为C的对象,则使用dynamic_cast和static_cast效果一样,都可以转换为C的指针

    A *ap = new B();

    C *cp = dynamic_cast<C *>(ap);

    C *cp1 = static_cast<C *>(ap);

    

    //若要将ap转换为dp,则都可以,只是dynamic_cast可以一次转换,static_cast需要一个过渡;dp的转换相当于cp1和dp1,相当于dp2

    D *dp = dynamic_cast<D *>(ap);

    D *dp1 = static_cast<D *>(cp1);

    D *dp2 = static_cast<D *>(static_cast<C *>(ap));

    delete ap; delete cp; delete cp1; delete dp; delete dp1; delete dp2;

    ap=cp=cp1=dp=dp1=dp2=NULL;  //防止野指针

    //如果ap指向的不为C的对象,则用dynamic_cast返回NULL,能够更早的禁止error的发生;情况与f3中一样!

}

dynamic_cast可用于进行交叉转换:

【代码7】


1

2

3

4

5

6

7

8

9

10

class E:public A{

    //类内定义

};

void f5(){  //交叉转换

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

    B *bp = new B();

    E *ep = dynamic_cast<E *>(bp);//正确,返回空指针

    E *ep1 = static_cast<E *>(bp);//错误,编译时出错

    //其他内容

}

dynamic_cast的讨论:

在探究 dynamic_cast 的设计意图之前,值得留意的是很多 dynamic_cast 的实现都相当慢。 例如,至少有一种通用的实现部分地基于对类名字进行字符串比较。如果你在一个位于四层深的单继承体系中的对象上执行 dynamic_cast,在这样一个实现下的每一个 dynamic_cast 都要付出相当于四次调用 strcmp 来比较类名字的成本。对于一个更深的或使用了多继承的继承体系,付出的代价会更加昂贵。

对 dynamic_cast 的需要通常发生在这种情况下:你要在一个你确信为派生类的对象上执行派生类的操作,但是你只能通过一个基类的指针或引用来操控这个对象。 有两个一般的方法可以避免这个问题:

  1. 使用存储着直接指向派生类对象的指针的容器,从而消除通过基类接口操控这个对象的需要。当然,这个方法不允许你在同一个容器中存储所有可能的基类的派生类的指针。为了与不同的窗口类型一起工作,你可能需要多个类型安全(type-safe)的容器。
  2. 通过一个基类的接口操控所有可能的 Window 派生类,就是在基类中提供一个让你做你想做的事情的虚函数。例如,尽管只有 SpecialWindows 能 blink,在基类中声明这个函数,并提供一个什么都不做的缺省实现或许是有意义的。

所以:避免强制转型的随意应用,特别是在性能敏感的代码中应用 dynamic_casts,如果一个设计需要强制转型,设法开发一个没有强制转型的侯选方案。 如果必须要强制转型,设法将它隐藏在一个函数中。客户可以用调用那个函数来代替在他们自己的代码中加入强制转型。

什么情况下使用dynamic_cast代替虚函数?

当基类代码不可知,需要在派生类里面新增新成员函数,但是又无法取得基类的源代码,不能在基类里面通过加虚函数接口调用新成员函数,可以通过dynamic_cast强制转换来获得调用。如:

【代码8】


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

class A{

public:

    virtual void fun(){

        cout<<"A::fun"<<endl;

    }

//其他代码未知,不能更改A的代码

};

class B:public A{

public:

    void fun(){

        cout<<"B::fun"<<endl;

    }

    void get(){

        cout<<"B::get"<<endl;

    }

};

 

int main()

{

    A *ap = new B;

    ap->fun ();

    (dynamic_cast<B*>(ap))->get (); //此时使用dynamic_cast获取调用

    if(B *o=dynamic_cast<B *>(ap))//此时使用dynamic_cast获取调用

        o->get();

    cin.get();

    return 0;

}

//输出为:

B::fun

B::get

typeid运算符允许向表达式提问:“你的对象是什么类型?”

typeid(e),其中e可以是任意表达式或类型的名字。操作结果是一个常量对象的引用。如果表达式为一个引用,则typeid返回该引用所引用对象的类型。如果e为数组或函数,则不会执行向指针的标准类型转换,如数组,会返回数组类型。当运算对象不属于类类型或者是一个不包含任何虚函数的类时,typeid运算符返回的是e的静态类型。当e是定义了至少一个虚函数的类的左值时,typeid的结果直到运行时才能求得。

当typeid作用于指针p时,返回的是该指针的静态编译时类型;当作用于指针所指的对象(*p)时,要在运行时才能求得返回类型。同时,如果指针p所指的对象的类型不含有虚函数,则p可以为一个无效的指针。否则,指针所指的对象(*p)将在运行时求值,此时p必须为一个有效的指针,如p此时为一个空指针,则typeid(*p)会抛出std::bad_typeid异常。

来自为知笔记(Wiz)

时间: 2024-10-11 00:29:34

C++学习之显示类型转换与运行时类型识别RTTI的相关文章

C++运行时类型识别——RTTI

RTTI 通过运行时类型识别--RTTI,程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类类型: 其主要通过两个操作符来实现: 1.typeid--返回指针或引用所指对象的实际类型: 2.dynamic_cast--将基类类型的指针或引用安全地转换为派生类型的指针或引用: dynamic_cast 当无法为基类增加虚函数,又要使用基类的指针或引用调用派生类的函数时,可以使用该操作符代替虚函数. 与dynamic_cast一起使用的指针必须是有效的--它必须为0或者指向一个对象

MFC中的运行时类型识别(RTTI)

RTTI是Runtime Type Identification的缩写,中文名为"运行时类型识别". MFC早在编译器支持RTTI之前,就有了这种能力.我们现在要以相同的手法,在Console程序中仿真出来.我希望我的类库具备IsKindOf 的能力,能在执行期侦测到某个对象是否属于某个类,并传回TRUE 或 FALSE.以形状 Shape为例 ,我希望: 即 长方形属于"长方形类",正方形属于"长方形类",圆不属于"长方形类"

运行时类型识别RTTI

1.RTTI的工作原理 例1. 用Class加载对象示例. package RTTI; public class Candy { static{ System.out.println("Loading Candy in static block."); } public static void main(String[] args) { System.out.println("Loading Candy in main method."); } } package

【转载】C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理

原文:C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理 运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换dynamic_cast. 1. typeid操作符的实现 1.1. 静态类型的情形 C++中支持使用typeid关键字获取对象类型信息,它的返回值类型是const std::type_info&,例: #include <typeinfo> #include <cassert>

Java基础之RTTI 运行时类型识别

运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于RTTI实现的.RTTI的功能主要是由Class类实现的. Class类 Class类是"类的类"(class of classes).如果说类是对象的抽象和集合的话,那么Class类就是对类的抽象和集合. 每一个Class类的对象代表一个其他的类.比如下面的程序中,Class类的对象c1代

运行时类型识别

1.RTTI 1)运行时类型识别RTTI(Run-Time Type Identification),它能够获取基类指针或引用所指向的对象的实际类型,在C++中,为了支持RTTI提供了两个运算符:typeid和dynamic_cast 2) 2.dynamic_cast 2.1概念 1)dynamic_cast运算符用于将基类的指针或引用安全地转换成派生类的指针或引用,这是安全的“向下转型”,至于“向上转型”,即派生类指针或引用转换为其基类指针或引用,本身就是安全的,尽管可以使用dynamic_

C++ Primer 学习笔记_102_特殊工具与技术 --运行时类型识别[续]

特殊工具与技术 --运行时类型识别[续] 三.RTTI的使用 当比较两个派生类对象的时候,我们希望比较可能特定于派生类的数据成员.如果形参是基类引用,就只能比较基类中出现的成员,我们不能访问在派生类中但不在基类中出现的成员. 因此我们可以使用RTTI,在试图比较不同类型的对象时返回假(false). 我们将定义单个相等操作符.每个类定义一个虚函数 equal,该函数首先将操作数强制转换为正确的类型.如果转换成功,就进行真正的比较:如果转换失败,equal 操作就返回 false. 1.类层次 c

C++杂记:运行时类型识别(RTTI)与动态类型转换原理

运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换dynamic_cast. 1. typeid操作符的实现 1.1. 静态类型的情形 C++中支持使用typeid关键字获取对象类型信息,它的返回值类型是const std::type_info&,例: #include <typeinfo> #include <cassert> struct B {} b, c; struct D : B {

C/C++杂记:运行时类型识别(RTTI)与动态类型转换原理

运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换dynamic_cast. 1. typeid操作符的实现 1.1. 静态类型的情形 C++中支持使用typeid关键字获取对象类型信息,它的返回值类型是const std::type_info&,例: #include <typeinfo> #include <cassert> struct B {} b, c; struct D : B {