C++中的类型判断typeid()操作与java中的 instanceof 做比较

烈酒焚心 提交于 2020-02-02 00:52:16

这是RTTI(运行阶段类型识别)的问题,c++有三个支持RTTI的元素:
1. dynamic_cast 操作符
    如果可能的话,dynamic_cast操作符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该操作符返回空指针。这是最常用的 RTTI组件,它不能回答“指针指向的是哪类对象”这样的问题,但他能回答“是否可以安全地将对象的地址赋给特定类型的指针”这样的问题。如:
class A{}
class B: public A{}
class C: public B{}
然后有下面的指针:
A *a = new A;
B *b = new B;
C *c = new C;
则:
C *cc1 = dynamic_cast<C*>(c);    //安全
C *cc2 = dynamic_cast<C*>(a);    //cc2是空指针
C *cc3 = dynamic_cast<c*>(b);    //cc3是空指针
B *bb = dynamic_cast<C*>(b);     //安全
注:只能将此RTTI用于包含虚函数的类层次结构,原因在于只有对于这种类层次结构,才应该将派生类对象的地址赋给基类指针。

2. typeid操作符
3. type_info结构,(须包含头文件<typeinfo>)
class A{}
class B: public A{}
class C: public B{}
然后有下面的指针:
A *pa = new A;
B *pb = new B;
C *pc = new C;
则:
typeid(C) == typeid(pc);  //值为true
typeid(B) == typeid(pc);  //值为false

type_info有一个name()成员函数,返回一个类名的字符串:
cout << typeid(*pc).name();  //打印C类的类名

注:typeid操作符和name()成员函数都适用于dynamic_cast和virtual函数不能处理的情况

以下总结:

  转自博客:http://www.cppblog.com/smagle/archive/2010/05/14/115286.html

  开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型,即允许“用指向基类的指针或引用来操作对 象”的程序能够获取到“这些指针或引用所指对象”的实际派生类型。在C++中,为了支持RTTI提供了两个操作符:dynamic_cast和 typeid。
    dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型,与之相对应的还有一个非安全的转换操作符 static_cast,因为这不是本文的讨论重点,所以这里不再详述,感兴趣的可以自行查阅资料。下面就开始今天我们的话题:typeid。
    typeid是C++的关键字之一,等同于sizeof这类的操作符。typeid操作符的返回结果是名为type_info的标准库类型的对象的引用(在头文件typeinfo中定义,稍后我们看一下vs和gcc库里面的源码),它的表达式有下图两种形式。

  如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。
    ISO C++标准并没有确切定义type_info,它的确切定义编译器相关的,但是标准却规定了其实现必需提供如下四种操作(在之后的章节中我会来分析type_info类文件的源码):

 t1 == t2  如果两个对象t1和t2类型相同,则返回true;否则返回false
 t1 != t2  如果两个对象t1和t2类型不同,则返回true;否则返回false
 t.name()  返回类型的C-style字符串,类型名字用系统相关的方法产生
 t1.before(t2)  返回指出t1是否出现在t2之前的bool值

    type_info类提供了public虚 析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的 对象。程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info的 友元)。type_info的name成员函数返回C-style的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名 并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。

  测试程序如下:

 

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Base {};
 5 class Derived: public Base {};
 6 
 7 int main()
 8 {
 9     Base b, *pb;
10     pb = NULL;
11     Derived d;
12 
13     cout << typeid(int).name() << endl
14          << typeid(unsigned).name() << endl
15          << typeid(long).name() << endl
16          << typeid(unsigned long).name() << endl
17          << typeid(char).name() << endl
18          << typeid(unsigned char).name() << endl
19          << typeid(float).name() << endl
20          << typeid(double).name() << endl
21          << typeid(string).name() << endl
22          << typeid(Base).name() << endl
23          << typeid(b).name()<<endl
24          << typeid(pb).name()<<endl
25          << typeid(Derived).name() << endl
26          << typeid(d).name()<<endl
27          << typeid(type_info).name() << endl;
28          
29     return 0;
30 }

 

不同的编译器输出如下:用MS的V8和GUN的GCC编译该段代码并运行,结果分别为下面的左右二图

 

      

  对比代码以及上面的文字描述,不知道各位是否已经有所明了(这里需要注意的是Base类的对象b和对象指针pb,他们的输出)。
    考虑到V8的输出很直观,所以我采用V8来做实验。下面对上面的代码稍微添加一点内容,如下:

 

Base *pb2 = dynamic_cast<Base *>(new Derived);
Base &b2 = d;
Base *pb3 = &d;
cout << typeid(pb2).name() <<endl//输出Base *
     << typeid(b2).name()<<endl //输出Base
     << typeid(pb3).name()<<endl//输出Base *
     << typeid(*pb3).name()<<endl;//输出Base

 

  这次Base含有虚函数,注意看结果,指针仍然是Base*的,尽管他们指向的是底层对象Derived,而这些Base对象的类型却是Derived的。
    因为指针pb3不是类类型,所以typeid就返回该指针pb3的指针类型Base *。而*pb3是一个类类型的表达式,而且该类带有虚函数,所以指出该pb3指向的底层对象的类型Derived。
    如果typeid操作符的操作数是至少包含一个虚拟函数的类类型时,并且该表达式是一个基类的引用,则typeid操作符指出底层对象的派生类类型。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!